Le load
message
Le moteur d'exécution envoie le load
message à chaque objet de classe, très peu de temps après le chargement de l'objet de classe dans l'espace d'adressage du processus. Pour les classes qui font partie du fichier exécutable du programme, le runtime envoie le load
message très tôt dans la durée de vie du processus. Pour les classes qui se trouvent dans une bibliothèque partagée (chargée dynamiquement), le moteur d'exécution envoie le message de chargement juste après que la bibliothèque partagée est chargée dans l'espace d'adressage du processus.
De plus, le runtime n'envoie load
à un objet de classe que si cet objet de classe lui-même implémente la load
méthode. Exemple:
@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
Le runtime envoie le load
message à l' Superclass
objet de classe. Il n'envoie pas le load
message à l' Subclass
objet de classe, même s'il Subclass
hérite de la méthode de Superclass
.
Le moteur d'exécution envoie le load
message à un objet de classe après qu'il a envoyé le load
message à tous les objets de superclasse de la classe (si ces objets de superclasse sont implémentés load
) et à tous les objets de classe dans les bibliothèques partagées vers lesquelles vous créez un lien. Mais vous ne savez pas encore quelles autres classes de votre propre exécutable ont été reçues load
.
Chaque classe que votre processus charge dans son espace d'adressage recevra un load
message si elle implémente la load
méthode, que votre processus utilise ou non la classe à une autre fin.
Vous pouvez voir comment le runtime recherche la load
méthode en tant que cas spécial dans le _class_getLoadMethod
of objc-runtime-new.mm
, et l'appelle directement depuis call_class_loads
in objc-loadmethod.mm
.
Le runtime exécute également la load
méthode de chaque catégorie qu'il charge, même si plusieurs catégories sur la même classe implémentent load
. C'est inhabituel. Normalement, si deux catégories définissent la même méthode sur la même classe, l'une des méthodes "gagnera" et sera utilisée, et l'autre méthode ne sera jamais appelée.
La initialize
méthode
Le runtime appelle la initialize
méthode sur un objet de classe juste avant d'envoyer le premier message (autre que load
ou initialize
) à l'objet de classe ou à toute instance de la classe. Ce message est envoyé en utilisant le mécanisme normal, donc si votre classe n'implémente pas initialize
, mais hérite d'une classe qui le fait, alors votre classe utilisera sa superclasse initialize
. Le runtime enverra d'abord le initialize
à toutes les superclasses d'une classe (si les superclasses n'ont pas déjà été envoyées initialize
).
Exemple:
@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;
}
Ce programme imprime deux lignes de sortie:
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
Puisque le système envoie la initialize
méthode paresseusement, une classe ne recevra le message que si votre programme envoie réellement des messages à la classe (ou à une sous-classe, ou à des instances de la classe ou des sous-classes). Et au moment où vous recevez initialize
, chaque classe de votre processus devrait déjà avoir reçu load
(le cas échéant).
La manière canonique de mettre en œuvre initialize
est la suivante:
@implementation Someclass
+ (void)initialize {
if (self == [Someclass class]) {
// do whatever
}
}
Le but de ce modèle est d'éviter de Someclass
se réinitialiser quand il a une sous-classe qui ne s'implémente pas initialize
.
Le runtime envoie le initialize
message dans la _class_initialize
fonction dans objc-initialize.mm
. Vous pouvez voir qu'il utilise objc_msgSend
pour l'envoyer, qui est la fonction normale d'envoi de messages.
Lectures complémentaires
Consultez les questions-réponses de Mike Ash sur ce sujet.
+load
est envoyé séparément pour les catégories; c'est-à-dire que chaque catégorie d'une classe peut contenir sa propre+load
méthode.initialize
sera correctement invoqué par uneload
méthode, si nécessaire, du fait de laload
référence à l'entité non initialisée. Cela peut (étrangement, mais raisonnablement) conduire àinitialize
courir avantload
! C'est ce que j'ai observé, de toute façon. Cela semble être contraire à "Et au moment où vous recevezinitialize
, chaque classe de votre processus devrait déjà avoir reçuload
(le cas échéant)."load
premier. Vous pouvez alors recevoirinitialize
pendant qu'ilload
est toujours en cours d'exécution.Cela signifie ne pas remplacer
+initialize
dans une catégorie, vous casserez probablement quelque chose.+load
est appelée une fois par classe ou catégorie implémentée+load
, dès que cette classe ou catégorie est chargée. Quand il dit "lié statiquement", cela signifie compilé dans le binaire de votre application. Les+load
méthodes sur les classes ainsi compilées seront exécutées au lancement de votre application, probablement avant son entréemain()
. Quand il dit "chargé dynamiquement", cela signifie chargé via des bundles de plugins ou un appel àdlopen()
. Si vous êtes sur iOS, vous pouvez ignorer ce cas.+initialize
est appelée la première fois qu'un message est envoyé à la classe, juste avant qu'elle ne traite ce message. Cela ne se produit (évidemment) qu'une seule fois. Si vous remplacez+initialize
une catégorie, l'une des trois choses suivantes se produira:C'est pourquoi vous ne devriez jamais remplacer
+initialize
dans une catégorie - en fait, il est assez dangereux d'essayer de remplacer une méthode dans une catégorie parce que vous n'êtes jamais sûr de ce que vous remplacez ou si votre propre remplaçant sera lui-même remplacé par une autre catégorie.BTW, un autre problème à considérer
+initialize
est que si quelqu'un vous sous-classe, vous serez potentiellement appelé une fois pour votre classe et une fois pour chaque sous-classe. Si vous faites quelque chose comme la configuration destatic
variables, vous voudrez vous prémunir contre cela: avecdispatch_once()
ou en testantself == [MyClass class]
.la source