Çözüm
Derleyici bu konuda bir uyarı yapıyor. Bu uyarının göz ardı edilmesi çok nadirdir ve etrafta dolaşmak kolaydır. Bunu nasıl yapacağınız aşağıda açıklanmıştır:
if (!_controller) { return; }
SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);
Veya daha tersine (okunması zor olsa da, gardiyan olmadan):
SEL selector = NSSelectorFromString(@"someMethod");
((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector);
açıklama
Burada olup bitenlerden, denetleyiciye denetleyiciye karşılık gelen yöntem için C işlev işaretçisini sormanız gerekir. Tümü NSObject
yanıt verir methodForSelector:
, ancak class_getMethodImplementation
Objective-C çalışma zamanında da kullanabilirsiniz (yalnızca bir protokol başvurunuz varsa yararlıdır id<SomeProto>
). Bu işlev işaretçileri IMP
s olarak adlandırılır ve basit typedef
düzen işlev işaretçileri ( id (*IMP)(id, SEL, ...)
) 1'dir . Bu, yöntemin gerçek yöntem imzasına yakın olabilir, ancak her zaman tam olarak eşleşmez.
Bir kez sahip olduktan sonra IMP
, ARC'nin ihtiyaç duyduğu tüm ayrıntıları içeren (iki gizli gizli argüman self
ve _cmd
her Objective-C yöntemi çağrısı dahil) bir işlev işaretçisine yayınlamanız gerekir . Bu üçüncü satırda ele alınır ( (void *)
sağ tarafta derleyiciye basitçe işaretçi türleri uyuşmadığından bir uyarı oluşturmamak için ne yaptığınızı bildiğinizi söyler).
Son olarak, fonksiyon işaretçisini 2 çağırırsınız .
Karmaşık Örnek
Seçici argüman aldığında veya bir değer döndürdüğünde, işleri biraz değiştirmeniz gerekir:
SEL selector = NSSelectorFromString(@"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = _controller ?
func(_controller, selector, someRect, someView) : CGRectZero;
Uyarı Nedeni
Bu uyarının nedeni ARC ile çalışma zamanının aradığınız yöntemin sonucu ile ne yapacağını bilmesi gerektiğidir. Sonuç şey olabilir: void
, int
, char
, NSString *
, id
, vb ARC normalde birlikte çalıştığınız nesne türü başlığındaki bu bilgileri alır. 3
ARC'nin dönüş değeri için dikkate alacağı sadece 4 şey var: 4
- Göz ardı olmayan nesne türleri (
void
, int
vs.)
- Nesne değerini koruyun, ardından artık kullanılmadığında bırakın (standart varsayım)
- Artık kullanılmadığında yeni nesne değerlerini serbest bırakın (
init
/ copy
ailesindeki veya ile ilişkilendirilen yöntemler ns_returns_retained
)
- Hiçbir şey yapma ve yerel kapsamda geçerli olacak döndürülen nesne değeri varsayılmaktadır (iç en bırakma havuz boşaltılır kadar olan atfedilen
ns_returns_autoreleased
)
methodForSelector:
To call, çağırdığı yöntemin dönüş değerinin bir nesne olduğunu varsayar, ancak bunu tutmaz / serbest bırakmaz. Böylece, nesnenizin yukarıdaki # 3'teki gibi serbest bırakılması gerekiyorsa bir sızıntı oluşturabilirsiniz (yani, çağırdığınız yöntem yeni bir nesne döndürür).
Bu dönüşü void
veya diğer nesne olmayan nesneleri çağırmaya çalıştığınız seçiciler için , uyarıcıyı yok saymak üzere derleyici özelliklerini etkinleştirebilirsiniz, ancak tehlikeli olabilir. Clang'ın yerel değişkenlere atanmamış dönüş değerlerini nasıl ele aldığının birkaç yinelemesinden geçtiğini gördüm. ARC etkinken, methodForSelector:
kullanmak istemeseniz bile döndürülen nesne değerini alamaması ve serbest bırakamamasının bir nedeni yoktur . Derleyicinin bakış açısından, sonuçta bir nesnedir. Bu, çağırdığınız yöntem someMethod
, bir nesne olmayan (dahil void
) döndürüyorsa , bir çöp işaretçisi değerinin korunmasına / bırakılmasına ve çökmesine neden olabileceği anlamına gelir .
Ek Bağımsız Değişkenler
Göz önünde bulundurulması gereken bir nokta, bunun aynı uyarının gerçekleşeceğidir performSelector:withObject:
ve bu yöntemin parametreleri nasıl tükettiğini bildirmemekle benzer sorunlarla karşılaşabilirsiniz. ARC, tüketilen parametreleri bildirmeye izin verir ve yöntem parametreyi tüketirse, muhtemelen sonunda bir zombi ve çökmeye bir mesaj gönderirsiniz. Köprülü döküm ile bu sorunu çözmenin yolları vardır, ancak gerçekten IMP
yukarıdaki ve işlev işaretçisi yöntemini kullanmak daha iyi olacaktır . Tüketilen parametreler nadiren sorun olduğundan, bunun ortaya çıkması muhtemel değildir.
Statik Seçiciler
İlginçtir ki, derleyici statik olarak beyan edilen seçicilerden şikayet etmeyecektir:
[_controller performSelector:@selector(someMethod)];
Bunun nedeni, derleyicinin derleme sırasında seçici ve nesne hakkındaki tüm bilgileri gerçekten kaydedebilmesidir. Hiçbir şey hakkında varsayımlarda bulunmaya gerek yoktur. (Bunu bir yıl önce kaynağa bakarak kontrol ettim, ancak şu anda bir referansım yok.)
Bastırma
Bu uyarının bastırılmasının gerekli olacağı bir durumu ve iyi kod tasarımını düşünmeye çalışırken boş kalıyorum. Birisi, bu uyarıyı susturmanın gerekli olduğu bir deneyim yaşadıysa (ve yukarıdakiler işleri düzgün bir şekilde ele almıyorsa) paylaşın.
Daha
Bunu NSMethodInvocation
da ele almak için bir inşa etmek mümkündür , ancak bunu yapmak çok daha fazla yazmayı gerektirir ve aynı zamanda daha yavaştır, bu yüzden bunu yapmak için çok az neden vardır.
Tarih
Ne zaman performSelector:
yöntemlerin aile önce Objective-C eklendi, ARC yoktu. ARC oluştururken Apple, geliştiricilere adlandırılmış bir seçici aracılığıyla rastgele mesajlar gönderirken belleğin nasıl ele alınması gerektiğini açık bir şekilde tanımlamak için diğer araçları kullanmaya yönlendirmek için bu yöntemler için bir uyarı oluşturulmasına karar verdi. Objective-C'de, geliştiriciler bunu ham işlev işaretçileri üzerinde C stili dökümler kullanarak yapabilirler.
Swift'in tanıtımı ile Apple ,performSelector:
yöntem ailesini "doğası gereği güvensiz" olarak belgeledi ve Swift için mevcut değil.
Zamanla, bu ilerlemeyi gördük:
- Objective-C'nin önceki sürümleri izin verir
performSelector:
(manuel bellek yönetimi)
- ARC'li Objective-C,
performSelector:
- Swift'in
performSelector:
bu yöntemlere "doğal olarak güvenli olmayan" erişimi yoktur ve bu belgeleri belgelemektedir
Bununla birlikte, adlandırılmış bir seçiciyi temel alan mesaj gönderme fikri "doğal olarak güvensiz" bir özellik değildir. Bu fikir, Objective-C ve diğer birçok programlama dilinde uzun süredir başarıyla kullanılmaktadır.
1 Tüm Objective-C yöntemlerinin iki gizli bağımsız değişkeni vardır self
ve _cmd
bunlar bir yöntemi çağırdığınızda örtük olarak eklenir.
2NULL
C'de bir işlev çağırmak güvenli değildir. Denetleyicinin varlığını kontrol etmek için kullanılan koruma, bir nesneye sahip olmamızı sağlar. Bu nedenle biz alırsınız biliyorum IMP
den methodForSelector:
(o olabilir ama _objc_msgForward
, mesaj iletme sistemine girişi). Temel olarak, gardiyan yerinde olduğunda, arayacak bir fonksiyonumuz olduğunu biliyoruz.
3 Aslında, nesneleri nesne olarak id
bildirirseniz ve tüm başlıkları içe aktarmazsanız yanlış bilgi alması mümkündür . Derleyicinin iyi olduğunu düşündüğü koddaki çökmelere neden olabilirsiniz. Bu çok nadirdir, ancak olabilir. Genellikle iki yöntem imzasından hangisinin seçileceğini bilmediğine dair bir uyarı alırsınız.
4 Daha fazla ayrıntı için alıkonan iade değerleri ve atılmayan getiri değerleri hakkındaki ARC referansına bakın.