12

Ich arbeite an diesem Code, der einige lange asynchrone Operation im Netz und wenn es fertig ist löst einen Abschlussblock, wo einiger Test ausgeführt wird, und wenn eine Variable einen bestimmten Wert wieder mit einer langwierigen Operation sofort starten bekommen soll:Wie zu beheben "Capturing 'Block' stark in diesem Block wird wahrscheinlich zu einem Retain-Zyklus führen"

-(void) performOperation 
{ 

    void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request){ 


     int variable=0; 

     // Do completion operation A 
     //... 
     //... 

     // Do completion operation B     
     //Get the variable value 

     if(variable>0){ 
      [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock]; 
     } 

    }; 

//Perform the lenhgty operation with the above completionBlock 
    [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock]; 

} 

-(void) doLengthyAsynchronousOperationWithCompletionBlock: completionBlock 
{ 
    //Do some lengthy asynchronous stuff 
} 

Mit diesem Code, den ich diese Warnung vom Compiler erhalten:

WARNING: Block pointer variable 'completionBlock' is uninitialized when caputerd by the block 

I geändert:

void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request) 

in:

__block void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request) 

aber ich habe diese andere Warnung:

WARNING 2: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle 

Wie kann ich dieses Problem beheben?

Dank

Nicola

+0

Werfen Sie einen Blick auf diese [Antwort] (http://stackoverflow.com/questions/7761074/arc-blocks-and-retain-cycles) –

Antwort

29

ACHTUNG: Blockzeigervariable 'completionBlock' ist nicht initialisiert, wenn

durch den Block erfasst

Dies geschieht, weil Block zu einem rekursiven Block initialisierten Variablen __block brauchen Lager.

  • Variablen innerhalb eines Blocks werden, es sei denn mit __block erklärt kopiert, in welchem ​​Fall sie als Referenz übergeben werden.
  • Wenn einer Blockvariablen ein rekursiver Block zugewiesen wird, erfolgt die Erstellung vor der Zuweisung, und diese Erstellung löst eine variable Kopie aus. Angesichts der Tatsache, dass die Variable noch nicht zugewiesen wurde, wird die kopierte Variable einen schlechten Wert haben, und es wird einen Absturz verursachen, wenn der Block ausgeführt wird. Wenn wir __block hinzufügen, wird der Block stattdessen mit einem Verweis auf die Variable erstellt. Dann wird die Variable für den erstellten Block initialisiert und der Block kann verwendet werden.

WARNUNG: Capturing ‚completionBlock‘ stark in diesem Block ist wahrscheinlich zu einem behalten Zyklus

Dies geschieht zu führen, weil ein Blockvariable ein starker Hinweis auf den Block, und der Block bezieht sich selbst auf die Variable (weil, wie wir zuvor gesehen haben, die Variable eine __block hat, so dass sie stattdessen referenziert wird, kopiert). So

wir brauchen

  • Ein schwacher Verweis auf die starken Variable innerhalb des Blockes.
  • Und eine starke Referenz außerhalb, um zu verhindern, dass der Block im Rahmen der Methode, in der er erstellt wird, freigegeben wird.
 
    void(^ completionBlock) (id obj, NSError *err, NSURLRequest *request); 
    void(^ __block __weak weakCompletionBlock) (id obj, NSError *err, NSURLRequest *request); 
    weakCompletionBlock = completionBlock = ^(id obj,NSError *err, NSURLRequest *request){ 
     [self lengthyAsyncMethod:weakCompletionBlock]; 
    }; 

Der Name doLengthyAsynchronousOperationWithCompletionBlock legt nahe, dass das Verfahren das Verfahren Umfang outlive kann, wo der Block erstellt wird. Da der Compiler einen als Argument übergebenen Block nicht kopiert, ist es Aufgabe dieser Methode, diesen Block zu kopieren. Wenn wir diesen Block mit blockweiseem Code verwenden (zB: dispatch_async()), geschieht dies automatisch.

Hätten wir diesen Block einer Instanzvariablen zugewiesen, benötigen wir einen @property(copy) und einen schwachen Verweis auf self innerhalb des Blocks, aber das ist nicht der Fall, also verwenden wir einfach self.

+0

Ich habe so etwas versucht und es scheint zu funktionieren: Ich speichere den Block In einer Kopiereigenschaft des Controllers übergebe ich dann die Eigenschaft als completionBlock. Nicola –

+0

du brauchst '__block' nicht auf' completionBlock', weil es niemals in einem Block verwendet wird! In der Tat, es ist nie irgendwo verwendet – newacct

+1

auch, "Selbst" ist nicht, was die OP-Warnung ist. Wir wissen nicht, ob "self" stark auf den Block verweist (das OP zeigt uns nicht genug Code, um das zu sehen), also ist es nicht sicher anzunehmen, dass es "__weak" sein muss. – newacct

Verwandte Themen