ARC etkin kodda “[bir nesneyi] güçlü bir şekilde yakalamanın büyük olasılıkla bir tutma döngüsüne yol açması” uyarısını düzeltin


141

ARC etkin kodda, blok tabanlı bir API kullanırken potansiyel bir tutma döngüsü hakkında bir uyarı nasıl düzeltilir?

Uyarı:
Capturing 'request' strongly in this block is likely to lead to a retain cycle

bu kod snippet'i tarafından üretilen:

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

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

Uyarı, nesnenin requestblok içindeki kullanımına bağlıdır .


1
Muhtemelen ASIHTTPRequest belgelerini kullanmak responseDatayerine kullanıyor rawResponseDataolmalısınız.
0x

Yanıtlar:


165

Kendime cevap veriyorum:

Belgeleri anlama şeklim, anahtar kelime kullanmanın blockve değişkeni blok içinde kullandıktan sonra nil olarak ayarlamanın iyi olduğunu, ancak yine de uyarıyı gösterdiğini söylüyor.

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

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

    }];

Güncelleme: ' _block' yerine '_ zayıf' anahtar kelimesi ile çalışmasını ve geçici bir değişken kullanmasını sağladı :

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

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

İOS 4'ü de hedeflemek istiyorsanız, __unsafe_unretainedyerine kullanın __weak. Aynı davranış, ancak nesne yok edildiğinde işaretçi otomatik olarak sıfır değerine ayarlamak yerine sarkık kalır.


8
ARC belgelerine dayanarak, ARC ve blokları kullanırken daha önce olduğu gibi aynı davranışı elde etmek için __unsafe_unretained __block'u birlikte kullanmanız gerektiği gibi geliyor.
Hunter

4
@SeanClarkHess: İlk iki satırı birleştirdiğimde şu uyarıyı alıyorum: "Korunmuş nesneyi zayıf değişkene atama; nesne atamadan sonra serbest bırakılacak"
Guillaume

1
@Guillaume yanıt için teşekkürler, bazı geçici değişkeni nasıl göz ardı ettim, denedim ve uyarılar gitti. Bunun neden işe yaradığını biliyor musunuz? Derleyiciyi yalnızca uyarıları bastırmak için kandırıyor mu yoksa uyarı artık geçerli değil mi?
Chris Wagner

2
Takip eden bir soru yayınladım: stackoverflow.com/questions/8859649/…
barfoon

3
Birisi neden __block ve __weak anahtar kelimelerine ihtiyacınız olduğunu açıklayabilir mi? Sanırım bir tutma döngüsü oluşturuluyor, ama görmüyorum. Ve geçici bir değişken oluşturmak sorunu nasıl çözer?
user798719

50

Sorun, içinde istek için güçlü bir başvuru olan bir blok atadığınız için oluşur. Blok otomatik olarak isteği saklar, bu nedenle orijinal istek döngü nedeniyle yeniden konumlandırılmaz. Mantıklı olmak?

Bu sadece garip çünkü istek nesnesini __block ile etiketliyorsunuz, böylece kendisine işaret edebiliyor. Sen zayıf bir referans oluşturarak bu sorunu giderebilirsiniz yanında ona.

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

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

__weak ASIHTTPRequest * wrequest = istek; benim için çalışmadı. Hata veriyor __block ASIHTTPRequest * blockRequest = request;
Ram

13

Kendini blokta tutmaya neden olur. Blok kendinden erişecek ve blokta kendini ifade edecektir. bu bir tutma döngüsü yaratacaktır.

Bunu zayıf bir referans oluşturarak çözmeyi deneyin. 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];

Bu doğru cevaptır ve şöyle belirtilmelidir
Benjamin

6

Bazı durumlarda xcode derleyicisinin tutma döngülerini tanımlamak için sorunları vardır, bu nedenle completionBlock'u tutmayacağınızdan eminseniz, böyle bir derleyici bayrağı koyabilirsiniz:

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

-(void)someMethod {
}

1
Bazıları bunun kötü tasarım olduğunu iddia edebilir, ancak bazen eşzamansız bir görevle bitene kadar bellekte takılan bağımsız nesneler yaratırım. Kendine güçlü bir başvuru içeren ve kasıtlı bir tutma döngüsü oluşturan bir completionBlock özelliği tarafından korunurlar. CompletionBlock, completionBlock öğesini serbest bırakan ve tutma döngüsünü kıran ve görev tamamlandığında nesnenin bellekten serbest bırakılmasına izin veren self.completionBlock = nil içerir. Cevabınız, bunu yaptığımda meydana gelen uyarıları susturmaya yardımcı olmak için kullanışlıdır.
hiperspazm

1
dürüst olmak gerekirse, birinin doğru ve derleyicinin yanlış olma şansı çok azdır. Bu yüzden sadece uyarıları aşmanın riskli bir iş olduğunu söyleyebilirim
Max MacLeod

3

Guillaume tarafından sağlanan çözümü denediğimde, Debug modunda her şey yolunda ama Release modunda çöküyor.

Hedefim iOS 4.3 olduğu için __weak değil __unsafe_unretained kullanılmadığını unutmayın.

SetCompletionBlock: "request" nesnesinde çağrıldığında kodum çöküyor: istek dağıtıldı ...

Bu nedenle, bu çözüm hem Debug hem de Release modlarında çalışır:

// 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];        
}];

İlginç bir çözüm. Neden Debug'da değil Release modunda çöktüğünü anladınız mı?
Valerio Santinelli

2
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
    blockRequest = nil;
// ....

}];

__weak ve __block reference arasındaki fark nedir?


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.