NSObject + yükle ve + başlat - Ne işe yarar?


116

Bir geliştiricinin + başlatmayı veya + yüklemeyi + geçersiz kılmasına neden olan koşulları anlamakla ilgileniyorum. Belgeler, bu yöntemlerin sizin için Objective-C çalışma zamanı tarafından çağrıldığını açıkça ortaya koymaktadır, ancak bu yöntemlerin belgelendirilmesinden gerçekten anlaşılan tek şey budur. :-)

Merakım Apple'ın örnek koduna (MVCNetworking) bakmaktan geliyor. Model sınıflarının bir +(void) applicationStartupyöntemi vardır. Dosya sistemi üzerinde bir miktar temizlik yapar, NSDefaults'u okur, vs ... ve NSObject'in sınıf metotlarını seçmeye çalıştıktan sonra, bu temizlik işinin + yüklemeye koyulması uygun görünebilir.

MVCNetworking projesini değiştirdim, App Delegate'deki çağrıyı + applicationStartup'a kaldırdım ve temizlik bitlerini + yüklemeye koydum ... bilgisayarım alev almadı, ancak bu doğru olduğu anlamına gelmez! Herhangi bir incelik, sorun ve özel bir kurulum yöntemi hakkında, + yükle veya + başlatmaya karşı çağırmanız gereken şeyler hakkında bir anlayış kazanmayı umuyorum.


+ Yük için dokümantasyon diyor ki:

Yükleme mesajı, hem dinamik olarak yüklenen hem de statik olarak bağlanan sınıflara ve kategorilere gönderilir, ancak yalnızca yeni yüklenen sınıf veya kategori yanıt verebilecek bir yöntem uygularsa.

Bu cümle kludgeydir ve tüm kelimelerin tam anlamını bilmiyorsanız ayrıştırılması zordur. Yardım!

  • "Hem dinamik olarak yüklü hem de statik olarak bağlı" ile ne kastedilmektedir? Bir şey dinamik olarak yüklenebilir VE statik olarak bağlanabilir mi, yoksa birbirini dışlar mı?

  • "... yeni yüklenen sınıf veya kategori," Hangi yöntem? Nasıl yanıtlanır?


İlklendirmeye gelince, dokümantasyon şunları söylüyor:

initialize, sınıf başına yalnızca bir kez çağrılır. Sınıf ve sınıf kategorileri için bağımsız başlatma gerçekleştirmek istiyorsanız, yükleme yöntemlerini uygulamanız gerekir.

Bunu, "sınıfı kurmaya çalışıyorsanız ... başlatma kullanma" anlamına geliyor. Tamam iyi. O halde başlatmayı ne zaman veya neden geçersiz kılmalıyım?

Yanıtlar:


185

loadmesaj

Çalışma zamanı load, işlemin adres alanına sınıf nesnesi yüklendikten hemen sonra mesajı her bir sınıf nesnesine gönderir . Programın çalıştırılabilir dosyasının bir parçası olan sınıflar için çalışma zamanı, loadmesajı işlemin ömrü içinde çok erken gönderir . Paylaşılan (dinamik olarak yüklenen) bir kitaplıkta bulunan sınıflar için, çalışma zamanı, paylaşılan kitaplık işlemin adres alanına yüklendikten hemen sonra yükleme mesajını gönderir.

Ayrıca, çalışma zamanı yalnızca loadbir sınıf nesnesinin kendisi loadyöntemi uygularsa bir sınıf nesnesine gönderir . Misal:

@interface Superclass : NSObject
@end

@interface Subclass : Superclass
@end

@implementation Superclass

+ (void)load {
    NSLog(@"in Superclass load");
}

@end

@implementation Subclass

// ... load not implemented in this class

@end

Çalışma zamanı, loadmesajı Superclasssınıf nesnesine gönderir . O mu değil göndermek loadiçin mesaj Subclassolsa bile, sınıf nesnesinin Subclassdevralır gelen yöntem Superclass.

Çalışma zamanı, loadmesajı loadsınıfın tüm üst sınıf nesnelerine (bu üst sınıf nesneleri uygularsa load) ve bağlandığınız paylaşılan kitaplıklardaki tüm sınıf nesnelerine gönderdikten sonra mesajı bir sınıf nesnesine gönderir . Ancak kendi çalıştırılabilir dosyanızda başka hangi sınıfların aldığını loadhenüz bilmiyorsunuz .

Sürecinizin kendi adres alanına yüklediği her sınıf, işleminizin sınıfı başka bir şekilde kullanıp kullanmadığına bakılmaksızın load, loadyöntemi uygularsa bir mesaj alır .

Sen çalışma zamanı arar nasıl görebilirsiniz loadözel bir durum olarak yöntemini _class_getLoadMethodait objc-runtime-new.mmve onu doğrudan çağırır call_class_loadsiçinde objc-loadmethod.mm.

Çalışma zamanı load, aynı sınıftaki birkaç kategori uygulasa bile, yüklediği her kategorinin yöntemini çalıştırır load. Bu alışılmadık bir durum. Normalde, iki kategori aynı sınıfta aynı yöntemi tanımlarsa, yöntemlerden biri "kazanacak" ve kullanılacak, diğer yöntem asla çağrılmayacaktır.

initializeYöntem

Çalışma zamanı , sınıf nesnesine veya sınıfın herhangi bir örneğine initializeilk mesajı ( loadveya dışında initialize) göndermeden hemen önce bir sınıf nesnesinde yöntemi çağırır . Bu mesaj normal mekanizma kullanılarak gönderilir, bu nedenle sınıfınız uygulanmazsa initialize, ancak bunu yapan bir sınıftan miras alırsa, sınıfınız kendi üst sınıflarını kullanacaktır initialize. Çalışma zamanı initializeönce bir sınıfın tüm üst sınıflarına gönderir (eğer üst sınıflar henüz gönderilmemişse initialize).

Misal:

@interface Superclass : NSObject
@end

@interface Subclass : Superclass
@end

@implementation Superclass

+ (void)initialize {
    NSLog(@"in Superclass initialize; self = %@", self);
}

@end

@implementation Subclass

// ... initialize not implemented in this class

@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        Subclass *object = [[Subclass alloc] init];
    }
    return 0;
}

Bu program iki satır çıktı yazdırır:

2012-11-10 16:18:38.984 testApp[7498:c07] in Superclass initialize; self = Superclass
2012-11-10 16:18:38.987 testApp[7498:c07] in Superclass initialize; self = Subclass

Sistem initializeyöntemi tembel olarak gönderdiğinden, programınız gerçekten sınıfa (veya bir alt sınıfa veya sınıfın veya alt sınıfların örneklerine) mesaj göndermediği sürece bir sınıf mesajı almaz. Ve aldığınız zamana kadar initialize, sürecinizdeki her sınıf load(uygunsa) zaten almış olmalıdır .

Uygulamanın kanonik yolu initializeşudur:

@implementation Someclass

+ (void)initialize {
    if (self == [Someclass class]) {
        // do whatever
    }
}

Bu modelin amacı, Someclassuygulanmayan bir alt sınıfa sahip olduğunda kendini yeniden başlatmaktan kaçınmaktır initialize.

Çalışma zamanı initialize, _class_initializeişlevdeki mesajı içinde gönderir objc-initialize.mm. objc_msgSendNormal mesaj gönderme işlevi olan göndermek için kullandığını görebilirsiniz .

daha fazla okuma

Check Mike Ash'in Cuma Q & A bu konuda.


26
+loadKategoriler için ayrı olarak gönderildiğini unutmayın ; yani, bir sınıftaki her kategori kendi +loadyöntemini içerebilir .
Jonathan Grynspan

1
Ayrıca , başlatılmamış varlığa referans yapılması nedeniyle gerekirse initializebir loadyöntem tarafından doğru şekilde çağrılacağını unutmayın load. Bu (tuhaf ama mantıklı bir şekilde) daha initializeönce koşmaya neden olabilir load! Ben de öyle gözlemledim. Bu, "Ve aldığınız zaman initialize, sürecinizdeki her sınıf load(uygunsa) çoktan almış olmalıydı ."
Benjohn

5
Önce sen alırsın load. Daha sonra alabilirsiniz initializeiken loadhala çalışıyor.
2014

1
@robmayoff, ilgili yöntemlerin içine [süper başlatma] ve [süper yük] satırları eklememiz gerekmiyor mu?
damithH

1
Bu genellikle kötü bir fikirdir, çünkü çalışma zamanı bu iki mesajı size göndermeden önce tüm üst sınıflarınıza zaten göndermiştir.
rob mayoff

17

Bunun anlamı, +initializebir kategoride geçersiz kılma , muhtemelen bir şeyi bozacaksın.

+loadsınıf veya uygular bu kategorideki başına bir kez denir +load, en kısa sürede bu sınıf veya kategori yüklenir. "Statik olarak bağlantılı" ifadesi, uygulamanızın ikili programında derlenmiş anlamına gelir. Bu +loadşekilde derlenen sınıflar üzerindeki yöntemler, uygulamanız başlatıldığında, muhtemelen girmeden önce çalıştırılacaktır main(). "Dinamik olarak yüklendi" dediğinde, eklenti paketleri veya bir çağrı yoluyla yüklendi anlamına gelir dlopen(). İOS kullanıyorsanız bu durumu göz ardı edebilirsiniz.

+initializesınıfa ilk kez bir mesaj gönderildiğinde, o mesajı işlemeden hemen önce denir. Bu (tabii ki) yalnızca bir kez olur. +initializeBir kategoride geçersiz kılarsanız , şu üç şeyden biri gerçekleşir:

  • kategori uygulamanız çağrılır ve sınıfın uygulaması
  • başka birinin kategori uygulaması çağrılır; yazdığın hiçbir şey yapmaz
  • kategoriniz henüz yüklenmedi ve uygulaması asla çağrılmadı.

Bu nedenle +initialize, bir kategoride asla geçersiz kılmamalısınız - aslında bir kategorideki herhangi bir yöntemi denemek ve değiştirmek oldukça tehlikelidir çünkü neyi değiştirdiğinizden veya kendi yerine koyma işleminizin başka bir kategori tarafından değiştirilip değiştirilmeyeceğinden asla emin olamazsınız.

BTW, dikkate alınması gereken başka bir konu +initializeda, eğer biri sizi alt sınıflara ayırırsa, potansiyel olarak sınıfınız için bir kez ve her alt sınıf için bir kez çağrılacaksınız. staticDeğişkenleri ayarlamak gibi bir şey yapıyorsanız, buna karşı korunmak isteyeceksiniz: ya dispatch_once()test ederek ya da test ederek self == [MyClass class].

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.