Jak naprawić " Przechwytywanie 'bloku' mocno w tym bloku prawdopodobnie doprowadzi do cyklu zatrzymania"
Pracuję nad tym kodem, który wykonuje długą asynchroniczną operację w sieci i po jej zakończeniu uruchamia blok zakończenia, w którym wykonywany jest test i jeśli zmienna otrzyma określoną wartość, kolejna długa operacja powinna rozpocząć się natychmiast:
-(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 otrzymuję Ostrzeżenie od 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 dostaję to drugie Ostrzeżenie:
WARNING 2: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle
Jak mogę naprawić to?
Dzięki
Nicola
1 answers
WARNING: Block pointer variable 'completionBlock' is unnitialized po przechwyceniu przez blok
Dzieje się tak, ponieważ zmienne blokowe inicjowane do rekurencyjnego bloku wymagają przechowywania __block
.
- zmienne wewnątrz bloku są kopiowane, chyba że są zadeklarowane przez
__block
, w którym to przypadku są przekazywane jako referencja. - gdy rekurencyjny blok jest przypisany do zmiennej blokowej, tworzenie odbywa się przed przypisaniem i takie tworzenie uruchamia zmienną przyjąłem. Biorąc pod uwagę, że zmienna nie została jeszcze przypisana, skopiowana zmienna będzie miała złą wartość i spowoduje awarię po uruchomieniu bloku.
- ale jeśli dodamy
__block
, blok zostanie utworzony z odniesieniem do zmiennej. Wtedy zmienna zostanie zainicjalizowana do utworzonego bloku i blok będzie gotowy do użycia.
WARNING: Przechwytywanie' completionBlock ' mocno w tym bloku jest prawdopodobne aby doprowadzić do cyklu zatrzymania
To dzieje się tak, ponieważ zmienna blokowa jest silnym odniesieniem do bloku, a sam blok odnosi się do zmiennej (ponieważ jak widzieliśmy wcześniej, zmienna ma __block
, więc jest odwołana zamiast kopiowana).
Więc potrzebujemy
- słabe odniesienie do silnej zmiennej wewnątrz bloku.
- i silne odniesienie na zewnątrz, aby zapobiec dealokacji bloku podczas zakresu 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
sugeruje że metoda może przetrwać zakres metody, w którym tworzony jest blok. Biorąc pod uwagę, że kompilator nie kopiuje bloku przekazanego jako argument, zadaniem tej metody jest skopiowanie tego bloku. Jeśli używamy tego bloku z kodem Block aware (np: dispatch_async()
), dzieje się to automatycznie.
Gdybyśmy przypisali ten blok do zmiennej instancji, potrzebowalibyśmy @property(copy)
i słabego odniesienia do self wewnątrz bloku, ale tak nie jest, więc po prostu używamy self.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-27 15:43:34