SORU
21 EKİM 2011, Cuma


Nasıl bir API uygularken blok kendi kendini yakalama önlemek mi?

Çalışan bir uygulamam var ve böyle büyük mükafat 4.2 ARK dönüştürme üzerinde çalışıyorum. Ön onay verilen uyarıları bir kuvvetle self bir blok döngüsü korumak için lider yakalama içerir. Basit bir kod örneği, sorunu göstermek için yaptım. Anlıyorum sanırım bu ama emin değilim ne demek "" ya da bu tür uygulamak önerilen yol senaryo. doğru

  • kendi sınıf MyAPİ bir örneğidir
  • aşağıdaki kod, yalnızca nesnelerle etkileşim ve blok sorumun ilgili göstermek için basitleştirilmiş
  • MyAPİ bu verileri uzak bir kaynak ve MyDataProcessor çalışır veri alır ve bir çıktı oluşturur varsayalım
  • işlemci blokları ile ilerleme ve devlet iletişim kurmak için yapılandırılır

kod örneği:

// code sample
self.delegate = aDelegate;

self.dataProcessor = [[MyDataProcessor alloc] init];

self.dataProcessor.progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

self.dataProcessor.completion = ^{
    [self.delegate myAPIDidFinish:self];
    self.dataProcessor = nil;
};

// start the processor - processing happens asynchronously and the processor is released in the completion block
[self.dataProcessor startProcessing];

""Ve/veya nasıl bu ARC kurallarına uymak için değiştirilmiş olmalıdır? neyi yanlış yapıyorum soru:

CEVAP
21 EKİM 2011, Cuma


Kısa cevap

Doğrudan self erişmek yerine, dolaylı olarak, korunacak bir referans erişim gerekir.Otomatik Referans kullanmadığınız Counting (ARC)yapabilirsiniz bunu yapmak için:

__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
    [dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}

__block anahtar kelime işaretleri değişkenleri değiştirilebilir iç blok (bunu yapmayacağız da, onlar otomatik olarak muhafaza blok tutulur (sürece kullanıyorsunuz ARC). Eğer bunu yaparsanız, başka bir şey MyDataProcessor örneği yayımlandıktan sonra bloğu çalıştırmak için denemek için gidiyor emin olmalısınız. (Kod yapısı göz önüne alındığında, bu bir sorun olmamalı.) Read more about __block.

ARC kullanıyorsanız, __block değişikliklerin anlam ve referansı __weak yerine bildirmeniz gerekir hangi korunur.

Uzun cevap

Hadi bu gibi bir kod var diyelim:

self.progressBlock = ^(CGFloat percentComplete) {
    [self.delegate processingWithProgress:percentComplete];
}

Buradaki sorun kendi kendine bloğu başvuru tutucu olduğunu; bu arada blok temsilci özelliğini getirme ve temsilci bir yöntemi göndermek için kendini bir başvuru tutmak gerekir. Eğer her şey senin app bültenleri başvuru için bu nesne, kendi korumak sayım olmayacak sıfır (çünkü taşıdır işaret) ve bloğu değil bir şey yapıyor yanlış (çünkü nesne işaret) ve çift nesneleri sızıntısı içine yığın bellek işgal eden ama sonsuza kadar ulaşılamaz olmadan bir hata ayıklayıcı. Çok kötü bir durum gerçekten.

Bu durum kolayca yerine bunu yaparak sabit olabilir:

id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
    [progressDelegate processingWithProgress:percentComplete];
}

Bu kod, kendi istinat blok, blok istinat temsilci ve yok döngüleri (görünür buradan; temsilci olabilir korumak bizim nesne ama bu bizim elimizde şu anda). Bu kod temsilci özelliğinin değeri blok oluşturulduğunda, yerine çalıştırıldığında baktı yakalanır çünkü aynı şekilde bir sızıntı riski olmaz. Bir yan etki, eğer bu bloğu oluşturulduktan sonra temsilci değiştirirseniz, blok hala eski temsilci güncelleme mesajları gönderir. Olsun o da olacak ya da uygulamaya bağlı değil.

Eğer bu davranış, bir sorun olsaydı bile, yine de senin durumunda bu numarayı kullanın.

self.dataProcessor.progress = ^(CGFloat percentComplete) {
    [self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};

Burada bir yerde onu almak için doğrudan bir yöntem çağrısında temsilciye self geçirmeden. Eğer blok türü tanımı üzerinde kontrol varsa, en iyi şey bir parametre olarak blok temsilci geçmek olacaktır:

self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
    [dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};

Bu çözüm döngüsü korumak önlerveher zaman geçerli temsilci çağırır.

Blok değiştirmek mi, olabiliranlaşma ile. Bir döngü bir uyarıdır korumak sebebi, bir hata değil, mutlaka uygulamanız için azap büyü yoktur. Eğer MyDataProcessor işlemi tamamlandığında blokları serbest bırakmak için mümkün ise, üst serbest deneyin önce, döngü kırılacak ve her şey düzgün bir şekilde temizlenecek. Eğer bundan emin olursan, o zaman yapılacak en doğru şey #pragma bir kod bloğu için uyarıları bastırmak için kullanmak olacaktır. (Ya da dosya başına derleyici bayrak kullanın. Ama tüm proje için Uyarı devre dışı bırakmak yok.)

Yukarıda da benzer bir hile kullanarak, bir başvuru ya da zayıf unretained ilan ve blok kullanarak içine bakmak olabilir. Ö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];
}

Her üç yukarıda size bir referans olmadan istinat sonuç, ama onlar tüm davranması biraz farklı: __weak denemek için sıfır referans olduğunda nesne serbest bırakıldığında; __unsafe_unretained bırak seninle geçersiz bir işaretçi; __block olur aslında ekleyin başka bir düzeyde Yönlendirme ve değiştirmenize olanak sağlayan değeri referans içinde blok (alakasız bu durumda, beri dp değil kullanılan başka bir yerde).

Nediriyiolamaz ne değiştirebilmek için ne bağlı olacaktır. Ama umarım bu devam etmek için nasıl bazı fikirler verdi.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Baby Big Mouth

    Baby Big Mou

    5 Mart 2013
  • Rugiagialia

    Rugiagialia

    1 Ocak 2008
  • TheForgottenGamer1

    TheForgotten

    28 AĞUSTOS 2009