2016-08-08 23 views
6

Ich versuche korrekt zu vermeiden, Zyklen mit Blöcken in Objective C zu behalten, und bin mir nicht sicher über verschachtelte Blöcke.Soll ich weakSelf in verschachtelten Blöcken verwenden?

Wenn ich einen einfachen Block wie folgt schreiben:

[self doSomethingWithBlock:^{ 
    [self doSomethingElse]; 
}]; 

der Compiler Fänge und warnt mich, dass diese Zyklen behalten verursachen könnte. Ich ändere es wie folgt um den Zyklus zu vermeiden:

__weak __typeof(self)weakSelf = self; 
[self doSomethingWithBlock:^{ 
    __strong __typeof(weakSelf)strongSelf = weakSelf; 
    [strongSelf doSomethingElse]; 
}]; 

Wenn ich so etwas schreiben:

[self doSomethingWithBlock:^(MyObject* object){ 
    [object doSomethingElseWithBlock:^{ 
     [self doYetAnotherThing]; 
    }]; 
}]; 

Der Compiler ist glücklich, aber ich bin nicht davon überzeugt, dass es sicher ist. Obwohl dazwischen ein Intermediär object ist, sieht es immer noch konzeptionell wie oben aus, aber jetzt ist es ein Zyklus mit 3 behält.

Sollte es stattdessen so sein?

[self doSomethingWithBlock:^(MyObject* object){ 
    __weak __typeof(self)weakSelf = self; 
    [object doSomethingElseWithBlock:^{ 
     __strong __typeof(weakSelf)strongSelf = weakSelf; 
     [strongSelf doYetAnotherThing]; 
    }]; 
}]; 

Oder so?

__weak __typeof(self)weakSelf = self; 
[self doSomethingWithBlock:^(MyObject* object){ 
    [object doSomethingElseWithBlock:^{ 
     __strong __typeof(weakSelf)strongSelf = weakSelf; 
     [strongSelf doYetAnotherThing]; 
    }]; 
}]; 
+1

Wird der Block an irgendeiner Stelle von selbst kopiert oder beibehalten? Oder wird es indirekt von einem anderen Objekt erhalten, das das Selbst behält? Wenn nicht, dann gibt es keinen Zyklus, egal wie tief die Verschachtelung ist. – danh

+0

@danh: Der Zyklus ist nicht das Problem. Selbst am Leben zu bleiben, wenn es nicht gebraucht wird, ist das Problem. – gnasher729

Antwort

4

In dieser Situation Sie zyklische Referenzen nicht besorgt sind. Worüber Sie sich Sorgen machen, ist eine Situation, in der das Objekt-Selbst nicht mehr benötigt wird, aber das Verwenden von Selbst innerhalb eines verschachtelten Blocks würde es unnötigerweise am Leben erhalten. Zum Beispiel, wenn Sie einen View-Controller haben, der verschwinden sollte, wenn die Ansicht vom Bildschirm entfernt wird, aber Sie ein Bild herunterladen, das Sie in der Controller-Ansicht anzeigen möchten. Wenn das Bild lange nach dem Ende der Ansicht eintrifft, möchten Sie den View-Controller nicht mehr aktiv.

Beste ist

__weak typeof (self) weakSelf = self; 

bevor die äußerste Methode aufrufen. Dann fügen Sie innerhalb jedes Blocks, der sich selbst verwenden soll,

typeof (self) strongSelf = weakSelf; 

hinzu und verwenden Sie strongSelf innerhalb dieses Blocks. Abhängig von der Situation möchten Sie vielleicht überprüfen, dass strongSelf zu diesem Zeitpunkt nicht null ist, aber das Senden von Nachrichten an strongSelf, wenn es null ist, hat keine Auswirkungen. Wenn Sie also nur Nachrichten senden und Eigenschaften abrufen oder festlegen, dann prüfen Sie für nil ist nicht notwendig.

Was passiert, wenn Sie dies nicht tun? Der Unterschied besteht darin, dass das Selbst unnötigerweise im innersten Block am Leben erhalten wird, wenn Sie sich selbst überall (oder nur im innersten Block) benutzen.

+0

Danke, das ist eine sehr gründliche Erklärung! – SaltyNuts

0

Schauen Sie sich die Antwort für diese question.

Ich würde diese

__weak typeof (self) weakSelf = self; 
[self doSomethingWithBlock:^(MyObject* object){ 
    [object doSomethingElseWithBlock:^{ 
     [weakSelf doYetAnotherThing]; 
    }]; 
}]; 
0

Xcode 8 tun Beta 4 unterstreicht die self Schlüsselwort, und warnt vor einer möglichen Zyklus behalten es innerhalb des Blocks verwendet zu haben.

Pro Apple Developer Connection Programmierung in Objective-C (Arbeiten mit Blöcken):

Starke Referenzzyklen vermeiden, wenn Selbst Capturing Wenn Sie in einem Block zu Capture selbst benötigen, wie zum Beispiel, wenn ein Rückruf Block definieren Es ist wichtig, die Auswirkungen des Speichermanagements zu berücksichtigen.

Blocks halten starke Verweise auf alle erfassten Objekte, einschließlich selbst, was bedeutet, dass es einfach ist, mit einem starken Bezug Zyklus zu beenden, wenn zum Beispiel ein Objekt eine Kopie Eigenschaft für einen Block hält , die sich selbst erfasst:

@interface XYZBlockKeeper : NSObject 
@property (copy) void (^block)(void); 
@end 
@implementation XYZBlockKeeper 
- (void)configureBlock { 
    self.block = ^{ 
     [self doSomething]; // capturing a strong reference to self 
           // creates a strong reference cycle 
    }; 
} 
... 
@end 

der Compiler wird Sie für ein einfaches Beispiel, wie diese warnen, sondern ein komplexeres Beispiel könnten mehrere starke Bezüge zwischen Objekte beinhalten die schaffen Zyklus, wodurch es schwieriger zu diagnostizieren.

dieses Problem zu vermeiden, ist es am besten Praxis eine schwache Referenz Selbst zu erfassen, wie folgt aus:

- (void)configureBlock { 
    XYZBlockKeeper * __weak weakSelf = self; 
    self.block = ^{ 
     [weakSelf doSomething]; // capture the weak reference 
            // to avoid the reference cycle 
    } 
} 

durch den schwachen Zeiger auf sich selbst aufnehmen, wird der Block ein nicht halten starke Beziehung zurück zum XYZBlockKeeper-Objekt.Wenn dieses Objekt freigegeben wird, bevor der Block aufgerufen wird, wird der weakSelf-Zeiger einfach auf Null gesetzt.

Diese Website bietet Berichten zufolge eine Möglichkeit, das self-Schlüsselwort schwach zu machen, wenn es in einem Block verwendet wird. es enthält auch Anweisungen für eine schwache Selbst oder Klassenobjekt früher stark, stark wieder zurückkehr:

https://coderwall.com/p/vaj4tg/making-all-self-references-in-blocks-weak-by-default

1

Sie sollten nichts schwach erfassen, nur weil Sie eine Warnung vom Compiler erhalten; Die Compilerwarnung ist nur raten; Es weiß nicht, wie die Methoden, die Sie aufrufen, die Referenzen bilden. Sie sollten dies nur tun, wenn Sie die Architektur der Referenzen verstehen und feststellen, dass ein Zyklus vorliegt, und feststellen, dass das Erfassen einer schwachen Referenz stattdessen das beabsichtigte Verhalten beibehält. Sie haben uns den Code -doSomethingWithBlock: nicht angezeigt. Es würde nur einen Retain-Zyklus erzeugen, wenn es innerhalb dieser Methode den Block einer Eigenschaft oder Instanzvariable von self zuweist. Tut es das oder nicht? Wenn nicht, dann gibt es keinen Rückhaltezyklus und es gibt keinen Sinn dafür, dass der äußere Block self schwach erfasst.

Angenommen, der äußere Block, der self schwach erfasst, ist richtig, die Beispiele, bei denen der äußere Block self stark erfasst, kommen nicht in Frage. Die verbleibenden Fragen würden lauten, ob der innere Block self (oder welche Version von self, beispielsweise strongSelf, angemessen ist) stark erfassen sollte. Mit anderen Worten, ob würden Sie so etwas wie dieses:

__weak __typeof(self) weakSelf = self; 
[self doSomethingWithBlock:^(MyObject* object){ 
    __strong __typeof(weakSelf) strongSelf = weakSelf; 
    if (strongSelf) { 
     [object doSomethingElseWithBlock:^{ 
      [strongSelf doYetAnotherThing]; 
     }]; 
    } 
}]; 

oder etwas ähnliches:

__weak __typeof(self) weakSelf = self; 
[self doSomethingWithBlock:^(MyObject* object){ 
    __strong __typeof(weakSelf) strongSelf = weakSelf; 
    if (strongSelf) { 
     [object doSomethingElseWithBlock:^{ 
      __strong __typeof(weakSelf) strongSelf = weakSelf; 
      if (strongSelf) { 
       [strongSelf doYetAnotherThing]; 
      } 
     }]; 
    } 
}]; 

Auch hier ist die wichtigste Frage, um zu bestimmen, ob es einen Zyklus, wenn der Innenblock erfasst behalten ist self stark. Es würde nur einen Retain-Zyklus geben, wenn [object doSomethingElseWithBlock:... den Block irgendwie einer Eigenschaft oder Instanzvariablen von self zuweist. Aber wie könnte es sein? Die Methode wird unter object, nicht self aufgerufen. Die Methode erhält self in keiner Weise.Wenn nicht etwas Kompliziertes vor sich geht, wird die Methode keiner Eigenschafts- oder Instanzvariablen von self zugewiesen, so dass es unwahrscheinlich ist, einen Retain-Zyklus zu erstellen. Dies bedeutet, dass der innere Block, der self schwach erfasst, nicht notwendig ist, um einen Rückhaltezyklus zu verhindern.

Aber ob der innere Block self schwach oder stark erfasst, könnte das Verhalten beeinflussen. Wenn nämlich der innere Block self schwach erfasst, könnte self zum Zeitpunkt der Ausführung des Blocks freigegeben werden. In diesem Fall wird [strongSelf doYetAnotherThing]; nicht ausgeführt, wohingegen wenn der innere Block self stark erfasst würde, würde self am Leben erhalten und [strongSelf doYetAnotherThing]; würde ausgeführt . Es kommt also darauf an, was -doYetAnotherThing tut. Wenn es eine UI-Operation unter self ausführt, bei der es sich um eine UI-Ansicht oder etwas handelt, macht es keinen Unterschied, ob Sie es in einer Ansicht ausführen, die nicht mehr angezeigt wird. Aber wenn es zum Beispiel etwas an das Netzwerk oder etwas sendet, kann es einen großen Unterschied machen, ob es ausgeführt wird oder nicht.