load
wiadomość
Środowisko wykonawcze wysyła load
komunikat do każdego obiektu klasy, wkrótce po załadowaniu obiektu klasy do przestrzeni adresowej procesu. W przypadku klas, które są częścią pliku wykonywalnego programu, środowisko wykonawcze wysyła load
komunikat na bardzo wczesnym etapie życia procesu. W przypadku klas, które znajdują się we współużytkowanej (ładowanej dynamicznie) bibliotece, środowisko wykonawcze wysyła komunikat ładowania zaraz po załadowaniu biblioteki współużytkowanej do przestrzeni adresowej procesu.
Ponadto środowisko wykonawcze wysyła load
do obiektu klasy tylko wtedy, gdy ten obiekt klasy sam implementuje load
metodę. Przykład:
@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
Środowisko wykonawcze wysyła load
komunikat do Superclass
obiektu klasy. To nie nie wysyła load
wiadomość do Subclass
obiektu klasy, choć Subclass
dziedziczy metodę od Superclass
.
Środowisko wykonawcze wysyła load
komunikat do obiektu klasy po wysłaniu load
komunikatu do wszystkich obiektów nadklasy klasy (jeśli te obiekty nadklasy są zaimplementowane load
) i wszystkich obiektów klas w bibliotekach współdzielonych, do których tworzysz łącze. Ale nie wiesz jeszcze, które inne klasy w twoim własnym pliku wykonywalnym otrzymały load
.
Każda klasa, którą twój proces ładuje do swojej przestrzeni adresowej, otrzyma load
komunikat, jeśli implementuje load
metodę, niezależnie od tego, czy twój proces używa tej klasy w inny sposób.
Można zobaczyć, jak środowisko wykonawcze wyszukuje load
metodę jako szczególny przypadek w _class_getLoadMethod
od objc-runtime-new.mm
, i wzywa go bezpośrednio z call_class_loads
w objc-loadmethod.mm
.
Środowisko uruchomieniowe uruchamia również load
metodę każdej ładowanej kategorii, nawet jeśli kilka kategorii na tym samym implementacji klasy load
. To jest niezwykłe. Zwykle, jeśli dwie kategorie definiują tę samą metodę w tej samej klasie, jedna z metod „wygra” i zostanie użyta, a druga nigdy nie zostanie wywołana.
initialize
Metoda
Środowisko wykonawcze wywołuje initialize
metodę na obiekcie klasy tuż przed wysłaniem pierwszej wiadomości (innej niż load
lub initialize
) do obiektu klasy lub jakichkolwiek instancji klasy. Ta wiadomość jest wysyłana przy użyciu normalnego mechanizmu, więc jeśli twoja klasa nie implementuje initialize
, ale dziedziczy po klasie, która to robi, wtedy twoja klasa użyje swojej nadklasy initialize
. Środowisko wykonawcze wyśle najpierw initialize
do wszystkich nadklas klasy (jeśli nadklasy nie zostały jeszcze wysłane initialize
).
Przykład:
@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;
}
Ten program wypisuje dwa wiersze wyniku:
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
Ponieważ system wysyła initialize
metodę leniwie, klasa nie otrzyma komunikatu, chyba że program faktycznie wyśle komunikaty do klasy (lub podklasy albo instancji klasy lub podklas). Zanim to zrobisz initialize
, wszystkie zajęcia w Twoim procesie powinny już być load
(jeśli to konieczne).
Kanoniczny sposób implementacji initialize
jest następujący:
@implementation Someclass
+ (void)initialize {
if (self == [Someclass class]) {
// do whatever
}
}
Celem tego wzorca jest uniknięcie Someclass
ponownej inicjalizacji, gdy ma podklasę, która nie jest implementowana initialize
.
Środowisko wykonawcze wysyła initialize
komunikat w _class_initialize
funkcji w formacie objc-initialize.mm
. Możesz zobaczyć, że używa objc_msgSend
do wysłania, co jest normalną funkcją wysyłania wiadomości.
Dalsza lektura
Zobacz piątkowe pytania i odpowiedzi Mike'a Asha na ten temat.
+load
jest wysyłany oddzielnie dla kategorii; oznacza to, że każda kategoria w klasie może zawierać własną+load
metodę.initialize
zostanie poprawnie wywołanaload
, jeśli to konieczne, ze względu naload
odwołanie do niezainicjowanej jednostki. Może to (co dziwne, ale rozsądne) prowadzić doinitialize
biegania wcześniejload
! W każdym razie to właśnie zauważyłem. Wydaje się, że jest to sprzeczne z „A do czasu, gdy otrzymujeszinitialize
, każda klasa w twoim procesie powinna już otrzymaćload
(jeśli to stosowne)”.load
pierwszy. Możesz wtedy otrzymać,initialize
gdyload
jest jeszcze uruchomiony.Oznacza to, że nie zastępuj
+initialize
w kategorii, prawdopodobnie coś zepsujesz.+load
jest wywoływana raz na klasę lub kategorię, która implementuje+load
, zaraz po załadowaniu tej klasy lub kategorii. Kiedy mówi „statycznie połączony”, oznacza to wkompilowanie do pliku binarnego Twojej aplikacji. Te+load
metody na zajęciach zebranych w ten sposób zostanie wykonane, gdy uruchamia app, prawdopodobnie zanim wejdziemain()
. Kiedy mówi „ładowane dynamicznie”, oznacza to ładowanie za pośrednictwem pakietów wtyczek lub wywołaniadlopen()
. Jeśli używasz iOS, możesz zignorować ten przypadek.+initialize
jest wywoływana przy pierwszym wysłaniu wiadomości do klasy, tuż przed jej obsługą. To (oczywiście) zdarza się tylko raz. Jeśli zastąpisz+initialize
kategorię, nastąpi jedna z trzech rzeczy:Dlatego nigdy nie powinieneś zastępować
+initialize
w kategorii - w rzeczywistości próba zastąpienia jakiejkolwiek metody w kategorii jest dość niebezpieczna, ponieważ nigdy nie masz pewności, co zastępujesz, ani czy Twój własny zamiennik sam zostanie zastąpiony przez inną kategorię.Przy okazji, kolejną kwestią do rozważenia
+initialize
jest to, że jeśli ktoś przejdzie do podklasy, potencjalnie zostaniesz wezwany raz dla Twojej klasy i raz dla każdej podklasy. Jeśli robisz coś takiego, jak konfigurowaniestatic
zmiennych, będziesz chciał się przed tym ustrzec: za pomocądispatch_once()
lub przez testowanieself == [MyClass class]
.źródło