Kısa cevap
self
Doğrudan erişim yerine, saklanmayacak bir referanstan dolaylı olarak erişmelisiniz. Otomatik Referans Sayımı (ARC) kullanmıyorsanız , bunu yapabilirsiniz:
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
__block
Bloğun içine değiştirilebilir anahtar kelime işaretleri değişkenler (biz bu yapmıyoruz) ama (sen ARC kullanmıyorsanız) da otomatik blok muhafaza edildiğinde tutulmaz. Bunu yaparsanız, MyDataProcessor örneği serbest bırakıldıktan sonra başka hiçbir şeyin bloğu yürütmeye çalışmadığından emin olmalısınız. (Kodunuzun yapısı göz önüne alındığında, bu bir sorun olmamalı.) Hakkında daha fazla bilgi edinin__block
.
ARC kullanıyorsanız , __block
değişikliklerin ve referansların semantiği korunacak, bu durumda bunu beyan etmelisiniz __weak
.
Uzun cevap
Diyelim ki böyle bir kodunuz var:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
Buradaki sorun, benliğin bloğa bir referans tutmasıdır; bu arada blok, delege özelliğini almak ve delege'ye bir yöntem göndermek için kendine bir referans tutmalıdır. Uygulamanızdaki her şey bu nesneye referansını serbest bırakırsa, tutma sayısı sıfır olmaz (blok ona işaret ettiği için) ve blok yanlış bir şey yapmaz (nesne nesneyi işaret ettiği için) vb. nesneler çifti yığına sızacak, hafızayı işgal edecek, ancak bir hata ayıklayıcı olmadan sonsuza kadar ulaşılamayacak. Gerçekten trajik.
Bu durum bunun yerine kolaylıkla düzeltilebilir:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
Bu kodda, benlik bloğu tutuyor, blok delege tutuyor ve döngü yok (buradan görülebilir; delege nesneyi koruyabilir, ancak şu anda bizim elimizden çıkmıyor). Delege özelliğinin değeri, yürütüldüğünde aramak yerine, blok oluşturulduğunda yakalandığından, bu kod aynı şekilde sızıntı riskini ortadan kaldırmaz. Bir yan etkisi, bu blok oluşturulduktan sonra delege değiştirirseniz, blok yine de eski delege için güncelleme iletileri gönderir olmasıdır. Bunun gerçekleşip gerçekleşmeyeceği uygulamanıza bağlıdır.
Bu davranıştan memnun olsanız bile, yine de bu hileyi sizin durumunuzda kullanamazsınız:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
Burada self
doğrudan yöntem çağrısında temsilciye geçiyorsunuz, bu yüzden oraya bir yere götürmelisiniz. Blok türünün tanımı üzerinde kontrolünüz varsa, en iyi şey, temsilciyi bloğa parametre olarak geçirmek olacaktır:
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
Bu çözüm, tutma döngüsünü önler ve her zaman geçerli temsilci çağırır.
Bloğu değiştiremezseniz, onunla başa çıkabilirsiniz . Koruma döngüsünün bir hata değil uyarı olması nedeni, uygulamanız için kıyameti mutlaka hecelememesidir. İşlem MyDataProcessor
tamamlandığında blokları serbest bırakabiliyorsa, ebeveyni serbest bırakmaya çalışmadan önce döngü bozulur ve her şey düzgün bir şekilde temizlenir. Bundan emin olabilirseniz, yapılacak doğru şey, #pragma
bu kod bloğunun uyarılarını bastırmak için a kullanmak olacaktır . (Veya dosya başına derleyici bayrağı kullanın. Ancak tüm proje için uyarıyı devre dışı bırakmayın.)
Ayrıca, benzer bir hile kullanarak, zayıf veya el değmemiş bir referans bildirerek ve bunu blokta kullanarak da bakabilirsiniz. Örneğin:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Yukarıdakilerin üçü de, sonucu tutmadan bir referans verecektir, ancak hepsi biraz farklı davranır: __weak
nesne serbest bırakıldığında referansı sıfırlamaya çalışacaktır; __unsafe_unretained
sizi geçersiz bir işaretçiyle bırakacaktır; __block
aslında başka bir dolaylılık düzeyi ekler ve referansın değerini blok içinden değiştirmenize izin verir ( dp
başka bir yerde kullanılmadığı için bu durumda alakasız ).
En iyisi , hangi kodu değiştirebileceğinize ve neleri değiştiremeyeceğinize bağlı olacaktır. Ama umarım bu size nasıl ilerleyeceğiniz konusunda bazı fikirler verir.