Das Problem hier ist, dass Ihr Block evaluteAndRecurse()
sich selbst erfasst, was bedeutet, dass, wenn es jemals kopiert werden soll (ich glaube nicht, wird es in Ihrem Fall, aber in etwas weniger trivialen Fällen kann es), dann es wird sich selbst behalten und daher für immer leben, da es nichts gibt, was den Rückhaltezyklus durchbrechen könnte.
Bearbeiten: Ramy Al Zuhouri machte einen guten Punkt, mit __unsafe_unretained
auf den einzigen Verweis auf den Block ist gefährlich. Solange der Block auf dem Stapel verbleibt, funktioniert dies. Wenn der Block jedoch kopiert werden muss (z. B. muss er in einen übergeordneten Bereich zurückverwandelt werden), wird die Zuweisung durch den Code __unsafe_unretained
aufgehoben. Der folgende Absatz wurde mit dem empfohlenen Ansatz aktualisiert:
Wahrscheinlich möchten Sie hier eine separate Variable verwenden, die mit __unsafe_unretained
markiert ist, die auch den Block enthält, und diese separate Variable erfassen. Dies verhindert, dass es sich selbst zurückhält. Sie könnten __weak
verwenden, aber da Sie wissen, dass der Block am Leben sein muss, wenn er aufgerufen wird, müssen Sie sich nicht mit dem (sehr geringen) Overhead einer schwachen Referenz befassen. Dadurch wird Ihr Code aussehen wie
NSArray*(^__block __unsafe_unretained capturedEvaluteAndRecurse)(UIView*);
NSArray*(^evaluateAndRecurse)(UIView*) = ^NSArray*(UIView *view) {
...
[myPassedChildren addObjectsFromArray:capturedEvaluateAndRecurse(subview)];
};
capturedEvaluateAndRecurse = evaluteAndRecurse;
Alternativ machen Sie einen Zeiger auf den Block erfassen könnte, die die gleiche Wirkung haben, sondern ermöglicht es Ihnen, statt nach den Zeiger vor dem Block Instanziierung zu greifen. Dies ist eine persönliche Präferenz. Es erlaubt Ihnen auch die __block
wegzulassen:
NSArray*(^evaluateAndRecurse)(UIView*);
NSArray*(^*evaluteAndRecursePtr)(UIView*) = &evaluateAndRecurse;
evaluateAndRecurse = ^NSArray*(UIView*) {
...
[myPassedChildren addObjectsFromArray:(*evaluateAndRecursePtr)(subview)];
};
Was benötigen die __block
, das ist ein anderes Thema. Wenn Sie nicht über __block
verfügen, erfasst die Blockinstanz tatsächlich den vorherigen Wert der Variablen. Denken Sie daran, dass bei der Erstellung eines Blocks alle erfassten Variablen, die nicht mit __block
gekennzeichnet sind, tatsächlich als const
Kopie ihres Status an dem Punkt gespeichert werden, an dem der Block instanziiert wird. Und da der Block vor erstellt wird, ist er der Variablen zugewiesen, dh er erfasst den Zustand der capturedEvaluteAndRecurse
Variable vor der Zuweisung, die nil
(unter ARC; andernfalls ist es Müllspeicher) sein wird.
Im Wesentlichen können Sie sich eine gegebene Blockinstanz vorstellen, die tatsächlich eine Instanz einer versteckten Klasse ist, die für jede erfasste Variable einen ivar hat. Also mit Ihrem Code, würde der Compiler behandelt im Grunde ist es so etwas wie:
// Note: this isn't an accurate portrayal of what actually happens
PrivateBlockSubclass *block = ^NSArray*(UIView *view){ ... };
block->stop = stop;
block->evaluteAndRecurse = evaluateAndRecurse;
evaluteAndRecurse = block;
Hoffentlich macht klar, warum es den vorherigen Wert von evaluateAndRecurse
anstelle des aktuellen Wertes erfaßt.
Warum bleibt der ARC-Zähler nach der Zuweisung von evaluateAndRecurse auf Null? –
@RamyAlZuhouri: Zählung behalten was? –
Von dem Block, da der Block __unsafe_unready ist, sollte nach der Zuweisung nicht freigegeben werden? –