Ich habe schon seit einiger Zeit Blöcke verwendet, aber ich denke, dass es Dinge gibt, die ich vermisse Speicherverwaltung in ARC und nicht-ARC-Umgebungen. Ich glaube, dass ein tieferes Verständnis mich dazu bringen wird, viele Speicherlecks aufzuheben.Objective C Speicherverwaltung mit Blöcken, ARC und nicht ARC
AFNetworking ist meine Hauptanwendung von Blöcken in einer bestimmten Anwendung. Meistens mache ich in einem Completion-Handler einer Operation etwas wie "[self.myArray addObject]".
Sowohl in ARC- als auch in nicht ARC-fähigen Umgebungen wird "self" gemäß this article from Apple beibehalten.
Das bedeutet, dass immer, wenn ein Completion-Block einer AFNetworking-Netzwerkoperation aufgerufen wird, self in diesem Block beibehalten und freigegeben wird, wenn dieser Block den Gültigkeitsbereich verlässt. Ich glaube, dass dies sowohl für ARC als auch für Nicht-ARC gilt. Ich habe sowohl das Leaks-Tool als auch den Static Analyzer ausgeführt, um Speicherlecks zu finden. Keine zeigte irgendwelche.
Allerdings war ich erst vor kurzem auf eine Warnung gestoßen, die ich nicht herausfinden konnte. Ich verwende ARC in diesem speziellen Beispiel.
ich zwei Instanzvariablen, die den Abschluss und Ausfall eines Netzbetriebs zeigen
@property (nonatomic, readwrite, copy) SFCompletionBlock completionBlock;
@property (nonatomic, readwrite, copy) SFFailureBlock failureBlock;
@synthesize failureBlock = _failureBlock;
@synthesize operation = _operation;
Irgendwo im Code, mache ich das:
[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id
responseObject) {
NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
_failureBlock(error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"nothing");
}];
Xcode beschwert sich über die Linie, die die Anrufe failureBlock, mit der Meldung "Capturing" self "stark in diesem Block führt wahrscheinlich zu einem Retain-Zyklus. Ich glaube, Xcode ist richtig: Der Fehler Block behält Selbst, und Selbst hält seine eigene Kopie des Blocks, so keiner der beiden wird freigegeben
Allerdings habe ich folgende Fragen/Beobachtungen.
1) Wenn ich _failureBlock (Fehler) in "self.failureBlock (Fehler)" (ohne Anführungszeichen) ändern, hört der Compiler auf sich zu beschweren. Warum das? Ist das ein Speicherleck, das der Compiler vermisst?
2) Was ist die beste Vorgehensweise beim Arbeiten mit Blöcken in ARC- und nicht ARC-fähigen Umgebungen, wenn Blöcke verwendet werden, die Instanzvariablen sind? Scheint, dass im Fall von Abschluss- und Fehlerblöcken in AFNetworking diese zwei Blöcke keine Instanzvariablen sind, so dass sie wahrscheinlich nicht in die Kategorie der Retain-Zyklen fallen, die ich oben beschrieben habe. Aber wenn Fortschrittsblöcke in AFNetworking verwendet werden, was kann getan werden, um Retain-Zyklen wie den obigen zu vermeiden?
Ich würde gerne die Gedanken anderer Leute über ARC und Nicht-ARC mit Blöcken und Problemen/Lösungen mit Speicherverwaltung hören. Ich finde diese Situationen fehleranfällig und ich denke, dass einige Diskussionen darüber notwendig sind, um die Dinge zu klären.
Ich weiß nicht, ob es darauf ankommt, aber ich verwende Xcode 4.4 mit dem neuesten LLVM.
Vielen Dank für diese Antwort. Das ist eine Menge Denkanstöße. Nebenbei bemerkt ist das, was Sie über den Callback und den Hintergrund-Thread gesagt haben, der Abstürze verursacht, korrekt. In meinem Fall werden jedoch alle Completion-Blöcke im Hauptthread aufgerufen, um solche Probleme zu vermeiden.Wie bei der anderen Sache, die Lösung "den Block von einer Methode zurückgeben", um Zyklen zu behalten, löst dies nicht das grundlegende Problem des Erstellens einer wiederverwendbaren Komponente, bei der der Aufrufer den Abschlussblock entscheidet. – csotiriou
Wenn Sie eine API mit Blockrückrufen verfügbar machen, können Sie nicht verhindern, dass Ihre Aufrufer eigene Aufbewahrungszyklen erstellen. Ihre API sollte darauf achten, die Callback-Blöcke so schnell wie möglich zu veröffentlichen, und sollte wahrscheinlich eine Abbruchmethode verfügbar machen, die auch die Callback-Blöcke freigibt. In der Praxis implementieren die meisten Anrufer die Callbacks inline und nicht in den Eigenschaften. In diesem Fall entspricht die Speichersemantik dem Zurückgeben des Blocks aus einer Methode. Der Aufbewahrungszyklus beginnt beim Aufruf der API und endet, wenn die API den Rückrufblock freigibt. –