capturer soi-même fortement dans ce bloc est susceptible de conduire à un cycle de rétention

Comment puis-je éviter cet avertissement dans xcode. Voici l’extrait de code:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100) queue:nil usingBlock:^(CMTime time) { current+=1; if(current==60) { min+=(current/60); current = 0; } [timerDisp(UILabel) setText:[NSSsortingng ssortingngWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line }]; 

    La capture de self ici avec votre access de propriété implicite à self.timerDisp – vous ne pouvez pas vous référer à self ou à des propriétés sur un bloc qui sera fortement conservé par vous- self .

    Vous pouvez contourner ce timerDisp en créant une référence faible à self avant d’accéder à timerDisp dans votre bloc:

     __weak typeof(self) weakSelf = self; [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100) queue:nil usingBlock:^(CMTime time) { current+=1; if(current==60) { min+=(current/60); current = 0; } [weakSelf.timerDisp setText:[NSSsortingng ssortingngWithFormat:@"%02d:%02d",min,current]]; }]; 
     __weak MyClass *self_ = self; // that's enough self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){ if (!error) { [self_ showAlertWithError:error]; } else { self_.items = [NSArray arrayWithArray:receivedItems]; [self_.tableView reloadData]; } }; 

    Et une chose très importante à retenir: n’utilisez pas de variables d’instance directement dans le bloc, utilisez-les comme propriétés d’un object faible, par exemple:

     self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){ if (!error) { [self_ showAlertWithError:error]; } else { self_.items = [NSArray arrayWithArray:receivedItems]; [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP } }; 

    et n’oubliez pas de faire:

     - (void)dealloc { self.loadingCompletionHandler = NULL; } 

    un autre problème peut apparaître si vous transmettez une copie faible de l’object non retenu par un object quelconque:

     MyViewController *vcToGo = [[MyViewCOntroller alloc] init]; __weak MyViewController *vcToGo_ = vcToGo; self.loadingCompletion = ^{ [vcToGo_ doSomePrecessing]; }; 

    Si vcToGo sera vcToGo et que ce bloc sera déclenché, je pense que vous obtiendrez un crash avec un sélecteur non reconnu dans une corbeille contenant la variable vcToGo_ maintenant. Essayez de le contrôler.

    Meilleure version

     __strong typeof(self) strongSelf = weakSelf; 

    Créez une référence forte à cette version faible en tant que première ligne de votre bloc. Si self existe toujours lorsque le bloc commence à s’exécuter et n’est pas revenu à zéro, cette ligne garantit sa persistance pendant toute la durée d’exécution du bloc.

    Donc tout serait comme ceci:

     // Establish the weak self reference __weak typeof(self) weakSelf = self; [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100) queue:nil usingBlock:^(CMTime time) { // Establish the strong self reference __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { [strongSelf.timerDisp setText:[NSSsortingng ssortingngWithFormat:@"%02d:%02d",min,current]]; } else { // self doesn't exist } }]; 

    J’ai lu cet article à plusieurs resockets. Ceci est un excellent article d’ Erica Sadun sur Comment éviter les problèmes lors de l’utilisation des blocs et NSNotificationCenter


    Mise à jour rapide:

    Par exemple, dans swift, une méthode simple avec un bloc de succès serait:

     func doSomeThingWithSuccessBlock(success: () -> ()) { success() } 

    Lorsque nous appelons cette méthode et que nous avons besoin de nous utiliser dans le bloc de succès. Nous utiliserons les caractéristiques de [weak self] et de guard let .

      doSomeThingWithSuccessBlock { [weak self] () -> () in guard let strongSelf = self else { return } strongSelf.gridCollectionView.reloadData() } 

    Cette danse dite forte-faible est utilisée par le projet open source populaire Alamofire .

    Pour plus d’informations, consultez swift-style-guide

    Dans une autre réponse, Tim a déclaré:

    vous ne pouvez pas vous référer à vous-même ou à des propriétés sur vous-même à partir d’un bloc qui sera fortement retenu par vous-même.

    Ce n’est pas tout à fait vrai. Vous pouvez le faire tant que vous interrompez le cycle à un moment donné. Par exemple, supposons que vous ayez un minuteur qui se déclenche et qui a un bloc qui se retient vous-même et vous gardez également une référence forte au minuteur en lui-même. Cela convient parfaitement si vous savez toujours que vous allez détruire la timer à un moment donné et interrompre le cycle.

    Dans mon cas tout à l’heure, j’ai eu cet avertissement pour le code qui a fait:

     [x setY:^{ [x doSomething]; }]; 

    Maintenant, je sais que clang ne produira cet avertissement que s’il détecte que la méthode commence par «set» (et un autre cas particulier que je ne mentionnerai pas ici). Pour moi, je sais qu’il n’ya aucun risque qu’il y ait une boucle de rétention, alors j’ai changé le nom de la méthode en «useY:» Bien sûr, cela pourrait ne pas être approprié dans tous les cas et Je pensais que cela valait la peine de noter ma solution au cas où cela aiderait les autres.

    Ajout de deux cents pour améliorer la précision et le style. Dans la plupart des cas, vous n’utiliserez qu’un ou plusieurs membres de vous- self dans ce bloc, probablement uniquement pour mettre à jour un curseur. Casting est exagéré. Au lieu de cela, il vaut mieux être explicite et ne lancer que les objects dont vous avez réellement besoin dans le bloc. Par exemple, s’il s’agit d’une instance de UISlider* , par exemple, _timeSlider , effectuez les opérations suivantes avant la déclaration de blocage:

     UISlider* __weak slider = _timeSlider; 

    Ensuite, utilisez simplement le slider à l’intérieur du bloc. Techniquement, ceci est plus précis car il réduit le cycle de rétention potentiel à l’object dont vous avez besoin, pas à tous les objects à self intérieur de vous- self .

    Exemple complet:

     UISlider* __weak slider = _timeSlider; [_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:nil usingBlock:^(CMTime time){ slider.value = time.value/time.timescale; } ]; 

    De plus, il est fort probable que l’object lancé sur un pointeur faible soit déjà un pointeur faible à self intérieur de self même, ce qui minimise ou élimine complètement la probabilité d’un cycle de rétention. Dans l’exemple ci-dessus, _timeSlider est en fait une propriété stockée en tant que référence faible, par exemple:

     @property (nonatomic, weak) IBOutlet UISlider* timeSlider; 

    En termes de style de codage, comme pour C et C ++, les déclarations de variables sont plus faciles à lire de droite à gauche. Déclarer SomeType* __weak variable dans cet ordre se lit plus naturellement de droite à gauche car: variable is a weak pointer to SomeType .