12

Pracuję nad tym kodem, który wykonuje długą, asynchroniczną operację w sieci, a po jej zakończeniu uruchamia blok kończący, gdzie jakiś test jest wykonywany i czy zmienna dostać pewną wartość kolejna długa operacja powinna rozpocząć się natychmiast:Jak naprawić "przechwytywanie 'bloku' mocno w tym bloku, prawdopodobnie doprowadzi do zatrzymania cyklu"

-(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 
} 

z tego kodu uzyskać to ostrzeżenie kompilatora:

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

zmieniłem:

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

w:

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

ale otrzymuję ten drugi ostrzeżenie:

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

Jak mogę rozwiązać ten problem?

Dzięki

Nicola

+0

Spójrz na to [odpowiedź] (http://stackoverflow.com/questions/7761074/arc-blocks-and-retain-cycles) –

Odpowiedz

29

UWAGA: zmienny wskaźnik Blok 'completionBlock' jest niezainicjowany gdy schwytany przez blok

Dzieje się tak dlatego, że zmienne bloku inicjowane do rekurencyjnego bloku trzeba __block przechowywanie.

  • Zmienne w bloku są kopiowane chyba że zadeklarowana z __block, w tym przypadku są one przekazywane jako odniesienie.
  • Gdy blok cykliczny jest przypisany do zmiennej blokowej, tworzenie następuje przed przypisaniem, a tworzenie takie uruchamia zmienną kopię. Biorąc pod uwagę, że zmienna nie została jeszcze przypisana, skopiowana zmienna będzie złą wartością i spowoduje awarię po uruchomieniu bloku.
  • Ale jeśli dodamy __block, blok zostanie utworzony z odniesieniem do zmiennej zamiast tego. Następnie zmienna zostanie zainicjowana do utworzonego bloku, a blok będzie gotowy do użycia.

UWAGA: przechwytywanie „completionBlock” mocno w bloku może doprowadzić do zatrzymania cyklu

Dzieje się tak, ponieważ zmienna jest blokowy wyraźne odniesienie do bloku, a blok sam odwołuje się do zmiennej (ponieważ, jak widzieliśmy wcześniej, zmienna ma wartość __block, więc jest przywoływana zamiast kopiowana).

Więc musimy

  • Słaby odniesienia do silnej zmiennej wewnątrz bloku.
  • I silne odwołanie na zewnątrz, aby zapobiec dezalokacji bloku w zakresie metody, w której został utworzony.
 
    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]; 
    }; 

Nazwa doLengthyAsynchronousOperationWithCompletionBlock sugerowano, że sposób może przeżyć zakres sposobu, w którym blok jest utworzona. Biorąc pod uwagę, że kompilator nie kopiuje bloku przekazanego jako argument, to za tę metodę odpowiada skopiowanie tego bloku. Jeśli używamy tego bloku z kodem blokującym świadomość (np. dispatch_async()), dzieje się to automatycznie.

Gdybyśmy przypisali ten blok do zmiennej instancji, potrzebowalibyśmy @property(copy) i słabego odniesienia do siebie wewnątrz bloku, ale tak nie jest, więc używamy tylko siebie.

+0

Próbowałem czegoś takiego i wygląda na to, że działa: zapisz blok we właściwości copy kontrolera, przekazuję właściwość jako completionBlock. Nicola –

+0

nie potrzebujesz '__block' na' completionBlock', ponieważ nigdy nie jest używany w bloku! w rzeczywistości nigdy nigdzie nie jest używany – newacct

+1

również, 'self' nie jest tym, o czym jest ostrzeżenie OP. Nie wiemy, czy 'self' silnie odwołuje się do bloku (OP nie pokazuje nam wystarczającej ilości kodu, aby to zobaczyć), więc nie można bezpiecznie założyć, że musi to być' __weak' – newacct