Was ist der Unterschied zwischen einer __schwachen und einer __blockischen Referenz?

80

Ich lese die Dokumentation von Xcode und hier ist etwas, das mich verwirrt:

__block typeof(self) tmpSelf = self;
[self methodThatTakesABlock:^ {
    [tmpSelf doSomething];
}];

Folgendes wird aus der Dokumentation kopiert:

Ein Block bildet einen starken Verweis auf Variablen, die er erfasst. Wenn Sie selfinnerhalb eines Blocks verwenden, bildet der Block eine starke Referenz auf self. Wenn also selfauch eine starke Referenz auf den Block vorhanden ist (was normalerweise der Fall ist), ergibt sich ein starker Referenzzyklus. Um den Zyklus zu vermeiden, müssen Sie einen schwachen (oder __block) Verweis auf sich selbst außerhalb des Blocks erstellen , wie im obigen Beispiel.

Ich verstehe nicht, was "schwach (oder __block)" bedeutet.

Ist

__block typeof(self) tmpSelf = self;

und

__weak typeof(self) tmpSelf = self;

genau das gleiche hier?

Ich habe ein weiteres Stück im Dokument gefunden:

Hinweis: Wenn Sie in einer Speicherbereinigungsumgebung sowohl __weakals auch __blockModifikatoren auf eine Variable anwenden , stellt der Block nicht sicher, dass sie am Leben bleibt.

Also bin ich total verwirrt.

HanXu
quelle

Antworten:

109

Aus den Dokumenten über __block

__block-Variablen befinden sich in einem Speicher, der vom lexikalischen Bereich der Variablen und allen im lexikalischen Bereich der Variablen deklarierten oder erstellten Blöcken und Blockkopien gemeinsam genutzt wird. Somit überlebt der Speicher die Zerstörung des Stapelrahmens, wenn Kopien der innerhalb des Rahmens deklarierten Blöcke über das Ende des Rahmens hinaus überleben (z. B. indem sie für eine spätere Ausführung irgendwo in die Warteschlange gestellt werden). Mehrere Blöcke in einem bestimmten lexikalischen Bereich können gleichzeitig eine gemeinsam genutzte Variable verwenden.

Aus den Dokumenten über __schwach

__weak gibt eine Referenz an, die das referenzierte Objekt nicht am Leben erhält. Eine schwache Referenz wird auf Null gesetzt, wenn keine starken Referenzen auf das Objekt vorhanden sind.

Es sind also technisch unterschiedliche Dinge. __block soll verhindern, dass Ihre Variable aus Ihrem externen Bereich in Ihren Blockbereich kopiert wird. __schwach ist ein selbstbegrenzender schwacher Zeiger.

Beachten Sie, dass ich technisch gesagt habe, weil sie für Ihren Fall (fast) dasselbe tun werden. Der einzige Unterschied besteht darin, ob Sie ARC verwenden oder nicht. Wenn Ihr Projekt ARC verwendet und nur für iOS4.3 und höher ist, verwenden Sie __weak. Es stellt sicher, dass die Referenz auf Null gesetzt ist, wenn die globale Bereichsreferenz irgendwie freigegeben wird. Wenn Ihr Projekt ARC nicht verwendet oder für ältere Betriebssystemversionen ist, verwenden Sie __block.

Hier gibt es einen subtilen Unterschied. Stellen Sie sicher, dass Sie ihn verstehen.

EDIT: Ein weiteres Teil des Puzzles ist __unsafe_unretained. Dieser Modifikator ist fast identisch mit __weak, jedoch für Umgebungen vor 4.3 Laufzeit. Es ist jedoch nicht auf Null gesetzt und kann Sie mit hängenden Zeigern verlassen.

Paul de Lange
quelle
1
Gilt dies weiterhin für iOS7 mit ARC? Ich habe einen Profiler ausgeführt und sehe, dass meine Controller freigegeben werden, auch wenn ich nicht __block oder __weak verwende und in einem Block auf mich selbst verweise.
Jay Q.
1
Wie wäre es, sie zusammen zu verwenden? __block _weak NSString *strEg;?
CyberMew
5

Im manuellen Referenzzählmodus __block id x; hat den Effekt, dass x nicht beibehalten wird. Im ARC-Modus __block id x; Standardmäßig wird x beibehalten (genau wie bei allen anderen Werten). Um das Verhalten des manuellen Referenzzählmodus unter ARC zu erhalten, können Sie __unsafe_unretained __block id x; verwenden. Wie der Name __unsafe_unretained impliziert, ist es jedoch gefährlich, eine nicht beibehaltene Variable zu haben (weil sie baumeln kann) und wird daher nicht empfohlen. Zwei bessere Optionen sind entweder __weak (wenn Sie iOS 4 oder OS X 10.6 nicht unterstützen müssen) oder den Wert __block auf nil zu setzen, um den Aufbewahrungszyklus zu unterbrechen.

Apple Docs

Andrei Shender
quelle
0

Neben anderen Antworten zu __blockvs __weakgibt es eine andere Möglichkeit, den Beibehaltungszyklus in Ihrem Szenario zu vermeiden.

@weakify(self);
[self methodThatTakesABlock:^ {
    @strongify(self);
    [self doSomething];
}];

Weitere Informationen zu @Weakify @Strongify Macro

BananZ
quelle
0

Wenn Sie self in block verwenden, sollten Sie __weak und nicht __block verwenden, da dies möglicherweise self beibehält .

Wenn Sie ein starkes Selbst brauchen, können Sie Folgendes verwenden:

__weak typeof(self) *weakSelf = self;
[self methodThatTakesABlock:^{
    if (weakSelf) {
        __strong typeof(self) *strongSelf = weakSelf;
        [strongSelf doSomething];
    }
}];
david72
quelle