In diesem Block wird es wahrscheinlich zu einem Retain-Zyklus führen, wenn man sich selbst stark in diesem Block erfasst

Wie kann ich diese Warnung in xcode vermeiden? Hier ist das Code-Snippet:

[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:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line }]; 

Die Erfassung von self hier kommt mit Ihrem impliziten Eigenschaftszugriff von self.timerDisp – Sie können nicht auf self oder Eigenschaften auf self innerhalb eines Blocks verweisen, der stark von self beibehalten wird.

Sie können dies timerDisp Sie vor dem Zugriff auf timerDisp in Ihrem Block einen schwachen Verweis auf self timerDisp :

 __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:[NSString stringWithFormat:@"%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]; } }; 

Und eine sehr wichtige Sache zu erinnern: Verwenden Sie keine Instanzvariablen direkt im Block, verwenden Sie es als Eigenschaften des schwachen Objekts, Beispiel:

 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 } }; 

und vergiss nicht zu tun:

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

Ein weiteres Problem kann auftreten, wenn Sie eine schwache Kopie eines von niemandem zurückgehaltenen Objekts passieren lassen:

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

Wenn vcToGo freigegeben wird und dann dieser Block ausgetriggers wird, glaube ich, dass Sie mit dem nicht erkannten Selektor auf einen Papierkorb vcToGo_ werden, der jetzt die Variable vcToGo_ enthält. Versuche es zu kontrollieren.

Bessere Version

 __strong typeof(self) strongSelf = weakSelf; 

Erstellen Sie eine starke Referenz auf diese schwache Version als erste Zeile in Ihrem Block. Wenn self immer noch existiert, wenn der Block mit der Ausführung beginnt und nicht auf Null zurückgegangen ist, stellt diese Zeile sicher, dass sie während der gesamten Ausführungsdauer des Blocks bestehen bleibt.

Also wäre das Ganze so:

 // 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:[NSString stringWithFormat:@"%02d:%02d",min,current]]; } else { // self doesn't exist } }]; 

Ich habe diesen Artikel viele Male gelesen. Dies ist ein ausgezeichneter Artikel von Erica Sadun über Probleme bei der Verwendung von Blöcken und NSNotificationCenter


Schnelle Aktualisierung:

Zum Beispiel wäre in swift eine einfache Methode mit Erfolg Block:

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

Wenn wir diese Methode aufrufen und im Erfolgsblock self . Wir werden die Features [weak self] und guard let .

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

Dieser sogenannte stark-schwache Tanz wird vom populären Open-Source-Projekt Alamofire .

Weitere Informationen finden Sie im swift-style-guide

In einer anderen Antwort sagte Tim:

Sie können nicht auf sich selbst oder Eigenschaften auf sich selbst innerhalb eines Blocks verweisen, der stark von sich selbst behalten wird.

Das ist nicht ganz richtig. Es ist in Ordnung für Sie, dies zu tun, solange Sie den Zyklus irgendwann unterbrechen. Angenommen, Sie haben einen Timer, der einen Block austriggers, der sich selbst enthält, und Sie behalten auch einen starken Bezug auf den Timer in self. Das ist völlig in Ordnung, wenn Sie immer wissen, dass Sie den Timer irgendwann zerstören und den Zyklus unterbrechen werden.

In meinem Fall hatte ich gerade diese Warnung für Code, der Folgendes tat:

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

Jetzt weiß ich zufällig, dass clang nur dann diese Warnung erzeugt, wenn es feststellt, dass die Methode mit “set” beginnt (und einem anderen Sonderfall, den ich hier nicht erwähnen werde). Für mich weiß ich, dass es keine Gefahr gibt, dass es eine Retain-Schleife gibt, also habe ich den Methodennamen in “useY:” geändert. Das ist natürlich nicht in allen Fällen angemessen und normalerweise werden Sie eine schwache Referenz verwenden wollen Ich denke, es lohnt sich, meine Lösung zu erwähnen, falls sie anderen hilft.

Hinzufügen von zwei Cent zur Verbesserung der Präzision und Stil. In den meisten Fällen werden Sie in diesem Block nur ein oder mehrere Elemente von self , wahrscheinlich nur um einen Schieberegler zu aktualisieren. Casting self ist Overkill. Stattdessen ist es besser, explizit zu sein und nur die Objekte zu erzeugen, die Sie wirklich innerhalb des Blocks benötigen. Zum Beispiel, wenn es eine Instanz von UISlider* , sagen wir _timeSlider , machen Sie einfach folgendes vor der _timeSlider :

 UISlider* __weak slider = _timeSlider; 

Dann benutzen Sie einfach den slider im Block. Technisch ist das genauer, da es den potenziellen Rückhaltezyklus auf nur das Objekt beschränkt, das Sie brauchen, nicht alle Objekte in sich self .

Vollständiges Beispiel:

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

Darüber hinaus ist das Objekt, das auf einen schwachen pointers geworfen wird, höchstwahrscheinlich bereits ein schwacher pointers innerhalb des self und minimiert oder beseitigt vollständig die Wahrscheinlichkeit eines Rückhaltezyklus. Im obigen Beispiel ist _timeSlider eigentlich eine Eigenschaft, die als schwache Referenz gespeichert ist, zB:

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

In Bezug auf den Codierungsstil, wie bei C und C ++, werden Variablendeklarationen besser von rechts nach links gelesen. Die Deklaration einer SomeType* __weak variable in dieser Reihenfolge liest sich von rechts nach links wie variable is a weak pointer to SomeType : variable is a weak pointer to SomeType .