Naprawiono ostrzeżenie „Mocne przechwycenie [obiektu] w tym bloku prawdopodobnie doprowadzi do cyklu przechowywania” w kodzie z włączoną funkcją ARC


141

W kodzie z włączoną funkcją ARC, jak naprawić ostrzeżenie o potencjalnym cyklu przechowywania podczas korzystania z interfejsu API opartego na blokach?

Ostrzeżenie:
Capturing 'request' strongly in this block is likely to lead to a retain cycle

utworzony przez ten fragment kodu:

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.rawResponseData error:nil];
    // ...
    }];

Ostrzeżenie jest powiązane z użyciem obiektu requestwewnątrz bloku.


1
Prawdopodobnie powinieneś używać responseDatazamiast rawResponseData, sprawdź dokumentację ASIHTTPRequest.
— 0xcewano

Odpowiedzi:


165

Odpowiadam sobie:

Z mojego rozumienia dokumentacji wynika, że ​​użycie słowa kluczowego blocki ustawienie zmiennej na zero po użyciu go w bloku powinno być w porządku, ale nadal wyświetla ostrzeżenie.

__block ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    request = nil;
// ....

    }];

Aktualizacja: sprawiłem, że działa ze słowem kluczowym „_ słaby” zamiast „ _block” i używając zmiennej tymczasowej:

ASIHTTPRequest *_request = [[ASIHTTPRequest alloc] initWithURL:...
__weak ASIHTTPRequest *request = _request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    // ...
    }];

Jeśli chcesz również kierować reklamy na iOS 4, użyj __unsafe_unretainedzamiast __weak. To samo zachowanie, ale wskaźnik pozostaje zawieszony zamiast być automatycznie ustawiany na zero, gdy obiekt zostanie zniszczony.


8
Na podstawie dokumentacji ARC wygląda na to, że musisz użyć __unsafe_unretained __block razem, aby uzyskać takie samo zachowanie jak wcześniej podczas korzystania z ARC i bloków.
— Hunter,

4
@SeanClarkHess: Kiedy łączę dwie pierwsze linie, otrzymuję ostrzeżenie: „Przypisywanie zachowanego obiektu do słabej zmiennej; obiekt zostanie zwolniony po przypisaniu”
— Guillaume

1
@ Guillaume dzięki za odpowiedź, trochę jak przeoczyłem zmienną tymczasową, spróbowałem i ostrzeżenia zniknęły. Czy wiesz, dlaczego to działa? Czy to tylko oszukanie kompilatora, aby powstrzymać ostrzeżenia, czy też ostrzeżenie jest już nieaktualne?
— Chris Wagner,


3
Czy ktoś może wyjaśnić, dlaczego potrzebujesz słów kluczowych __block i __weak? Wydaje mi się, że powstaje cykl retainy, ale go nie widzę. W jaki sposób utworzenie zmiennej tymczasowej rozwiązuje problem?
— user798719

50

Ten problem występuje, ponieważ przypisujesz blok do żądania, który ma silne odniesienie do żądania. Blok automatycznie zachowa żądanie, więc pierwotne żądanie nie zostanie zwolnione z powodu cyklu. Ma sens?

To po prostu dziwne, ponieważ tagujesz obiekt żądania za pomocą __block, aby mógł odwoływać się do siebie. Możesz to naprawić, tworząc obok niego słabe odniesienie .

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...];
__weak ASIHTTPRequest *wrequest = request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
    // ...
    }];

__weak ASIHTTPRequest * wrequest = request; nie działa dla mnie. Podając błąd użyłem __block ASIHTTPRequest * blockRequest = request;
— Ram G.

13

Powoduje to poprzez zatrzymanie siebie w bloku. Blok będzie dostępny od siebie, a do siebie odnosi się blok. stworzy to cykl utrzymania.

Spróbuj rozwiązać ten problem, tworząc słabe odniesienie do self

__weak typeof(self) weakSelf = self;

operationManager = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

    [weakSelf requestFinishWithSucessResponseObject:responseObject withAFHTTPRequestOperation:operation andRequestType:eRequestType];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [weakSelf requestFinishWithFailureResponseObject:error withAFHTTPRequestOperation:operation andRequestType:eRequestType];
}];
[operationManager start];

To jest poprawna odpowiedź i należy ją tak zapisać
— Benjamin,

6

Czasami kompilator xcode ma problemy z identyfikatorem zachowań cykli, więc jeśli jesteś pewien, że nie zachowujesz kompletnego elementu, możesz umieścić flagę kompilatora w następujący sposób:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"

-(void)someMethod {
}

1
Niektórzy mogą twierdzić, że to zły projekt, ale czasami tworzę niezależne obiekty, które wiszą w pamięci, dopóki nie zostaną ukończone z zadaniem asynchronicznym. Są one zachowywane przez właściwość CompleteBlock, która zawiera silne odniesienie do siebie, tworząc celowy cykl przechowywania. CompletionBlock zawiera self.completionBlock = nil, co zwalnia komendę CompleteBlock i przerywa cykl przechowywania, umożliwiając zwolnienie obiektu z pamięci po zakończeniu zadania. Twoja odpowiedź jest przydatna, aby pomóc wyciszyć ostrzeżenia, które pojawiają się, gdy to robię.
— hiperspazm

1
szczerze mówiąc, szanse na to, że ktoś ma rację, a kompilator się myli, są bardzo małe. Więc powiedziałbym, że przekroczenie ostrzeżeń jest ryzykownym biznesem
— Max MacLeod

3

Kiedy wypróbowuję rozwiązanie dostarczone przez Guillaume, wszystko jest w porządku w trybie debugowania, ale ulega awarii w trybie wydania.

Zauważ, że nie używaj __weak, ale __unsafe_unretained, ponieważ moim celem jest iOS 4.3.

Mój kod ulega awarii, gdy setCompletionBlock: jest wywoływana na obiekcie „request”: żądanie zostało cofnięte ...

Tak więc to rozwiązanie działa zarówno w trybie debugowania, jak i wydania:

// Avoiding retain cycle :
// - ASIHttpRequest object is a strong property (crashs if local variable)
// - use of an __unsafe_unretained pointer towards self inside block code

self.request = [ASIHttpRequest initWithURL:...
__unsafe_unretained DataModel * dataModel = self;

[self.request setCompletionBlock:^
{
    [dataModel processResponseWithData:dataModel.request.receivedData];        
}];

Ciekawe rozwiązanie. Czy wiesz, dlaczego ulega awarii w trybie wydania, a nie w debugowaniu?
— Valerio Santinelli


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.