19

Ich habe Request mit Block. Aber der Compiler gibt eine Warnung ausDie starke Aufnahme von "self" in diesem Block führt wahrscheinlich zu einem Retain-Zyklus

„Capturing‚Selbst‘stark in diesem Block ist wahrscheinlich ein führen Zyklus behalten“

__weak typeof(self) weakSelf = self; 
[generalInstaImage setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:data[@"images"][@"low_resolution"][@"url"]]] placeholderImage:[UIImage imageNamed:@"Default"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { 
    NSLog(@"success"); 
    [generalInstaImage setImage: image]; 
    [weakSelf saveImage:generalInstaImage.image withName:data[@"id"]]; 

    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { 
     NSLog(@"fail"); 
}]; 

Ich versuche Beispiel schreibe wie weakSelf.generalInstaImage, aber dann die Compiler erzeugt einen Fehler und kompiliert nicht.

+0

Was ist der Fehler? versuchen Sie es mit 'ID' oder dem tatsächlichen Klassennamen anstelle von' typeof (self) ' – Fonix

Antwort

60

Betrachten Sie diese Warnung:

self stark in diesem Block wahrscheinlich ein Zyklus führen wird Capturing behalten

Wenn Sie die obige Warnung erhalten, sollten Sie Ihren Block für eine Bewertung

  • alle expliziten Verweise auf self; oder
  • alle impliziten Verweise auf self, die durch Referenzieren von Instanzvariablen verursacht werden.

Stellen wir uns vor, dass wir eine einfache Klasse Eigenschaft haben, dass ein Block war (dies wird die gleiche „behalten Zyklus“ Warnungen wie Ihre Frage erleben, aber meine Beispiele ein wenig einfacher halten):

@property (nonatomic, copy) void (^block)(void); 

Und nehmen wir an, wir eine andere Klasse Eigenschaft hatten wir in unserem Block verwenden wollte:

@property (nonatomic, strong) NSString *someString; 

Wenn Sie innerhalb des Blocks (in meinem Beispiel unten in Prozess der Zugriff auf diese Eigenschaft), Sie verweisen self wird natürlich erhalten, dass über die behalten Zyklus Risikowarnung:

self.block = ^{ 
    NSLog(@"%@", self.someString); 
}; 

, die über das Muster behoben wird vorgeschlagen, dass Sie, und zwar:

__weak typeof(self) weakSelf = self; 

self.block = ^{ 
    NSLog(@"%@", weakSelf.someString); 
}; 

Weniger offensichtlich, werden Sie auch die „Zyklus behalten“ erhalten Warnung, wenn Sie verweisen auf ein Instanzvariable der Klasse innerhalb des Blocks, zum Beispiel:

self.block = ^{ 
    NSLog(@"%@", _someString); 
}; 

Dies, da die _someString Instanzvariable trägt eine implizite Referenz zu self, und ist eigentlich äquivalent zu:

self.block = ^{ 
    NSLog(@"%@", self->_someString); 
}; 

Sie geneigt sein könnte, um zu versuchen schwache Selbstmuster zu übernehmen hier auch, aber man kann es nicht.Wenn Sie die weakSelf->_someString Syntaxmuster versuchen, wird die Compiler Sie darüber warnen:

Dereferenzierung eines __weak Zeiger nicht wegen möglichem Nullwert durch Racebedingung verursachte erlaubt ist, weist es strong variable erste

Sie lösen damit diese durch die weakSelf Muster verwenden, sondern auch ein lokales strong Variable innerhalb des Blockes erstellen und verwenden zu dereferenzieren den Instanzvariable:

__weak typeof(self) weakSelf = self; 

self.block = ^{ 
    __strong typeof(self) strongSelf = weakSelf; 

    if (strongSelf) { 
     NSLog(@"%@", strongSelf->_someString); 

     // or better, just use the property 
     // 
     // NSLog(@"%@", strongSelf.someString); 
    } 
}; 

Nebenbei, diese Erstellung einer lokalen strong Referenz, strongSelf, innerhalb des Blocks hat auch andere Vorteile, nämlich, wenn der Abschlussblock asynchron auf einem anderen Thread ausgeführt wird, müssen Sie sich keine Gedanken über self sein Die Zuweisung wird aufgehoben, während der Block ausgeführt wird, was zu unbeabsichtigten Konsequenzen führt.

Diese weakSelf/strongSelf Muster ist sehr nützlich, wenn sie mit Blockeigenschaften zu tun, und Sie wollen verhindern, dass Zyklen beibehalten (auch bekannt als starke Referenzzyklen), aber zugleich sichergestellt wird, dass self nicht in der Mitte der Ausführung der ausgeplant werden Abschlussblock.

FYI, diskutiert Apple dieses Muster in der "nicht trivialen Zyklen" -Diskussion weiter unten im Use Lifetime Qualifiers to Avoid Strong Reference Cycles Abschnitt der Transitioning to ARC Release Notes.


Sie berichten, dass Sie einige „Fehler“ erhalten, wenn Sie weakSelf.generalInstaImage in Ihrem Beispiel verwiesen. Dies ist der richtige Weg, um diese "Retain Cycle" Warnung zu lösen. Wenn Sie also eine Warnung erhalten haben, sollten Sie diese mit uns teilen und uns zeigen, wie Sie die Eigenschaft deklariert haben.

+3

Sehr vollständige Antwort! +1 –

+1

Danke :) Ich habe – Alexander

+0

verstanden "wird eine Ausnahme generieren" Nein, es wird keine Ausnahme generiert. Dies führt zu undefiniertem Verhalten (wahrscheinlich ein Segmentierungsfehler). – newacct

2

Verwenden __unsafe_unretained typeof(self) weakSelf = self

+3

Dies kann die Compiler-Warnung eliminieren, kann aber verheerende Folgen haben, wenn "self" zum Zeitpunkt des Aufrufs des Blocks aufgehoben wurde, da Sie einen unkontrollierten Zeiger auf ein freigegebenes Objekt haben. Dies ist, auf die Gefahr hin, das Offensichtliche zu sagen, sehr _unsicher._ – Rob

Verwandte Themen