El load
mensaje
El tiempo de ejecución envía el load
mensaje a cada objeto de clase, muy poco después de que el objeto de clase se cargue en el espacio de direcciones del proceso. Para las clases que forman parte del archivo ejecutable del programa, el tiempo de ejecución envía el load
mensaje muy temprano en la vida útil del proceso. Para las clases que están en una biblioteca compartida (cargada dinámicamente), el tiempo de ejecución envía el mensaje de carga justo después de que la biblioteca compartida se cargue en el espacio de direcciones del proceso.
Además, el tiempo de ejecución solo envía load
a un objeto de clase si ese objeto de clase en sí mismo implementa el load
método. Ejemplo:
@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
El tiempo de ejecución envía el load
mensaje al Superclass
objeto de clase. No , no enviar el load
mensaje al Subclass
objeto de clase, a pesar de que Subclass
hereda el método de Superclass
.
El tiempo de ejecución envía el load
mensaje a un objeto de clase después de haber enviado el load
mensaje a todos los objetos de superclase de la clase (si esos objetos de superclase se implementan load
) y todos los objetos de clase en las bibliotecas compartidas a las que se vincula. Pero aún no sabe qué otras clases de su propio ejecutable han recibido load
.
Cada clase que su proceso carga en su espacio de direcciones recibirá un load
mensaje, si implementa el load
método, independientemente de si su proceso hace algún otro uso de la clase.
Puede ver cómo el tiempo de ejecución busca el load
método como un caso especial en _class_getLoadMethod
of objc-runtime-new.mm
y lo llama directamente desde call_class_loads
adentro objc-loadmethod.mm
.
El tiempo de ejecución también ejecuta el load
método de cada categoría que carga, incluso si se implementan varias categorías en la misma clase load
. Esto es inusual. Normalmente, si dos categorías definen el mismo método en la misma clase, uno de los métodos "ganará" y se usará, y el otro método nunca será llamado.
El initialize
método
El tiempo de ejecución llama al initialize
método en un objeto de clase justo antes de enviar el primer mensaje (distinto de load
o initialize
) al objeto de clase o cualquier instancia de la clase. Este mensaje se envía utilizando el mecanismo normal, por lo que si su clase no se implementa initialize
, pero hereda de una clase que lo hace, entonces su clase usará su superclase initialize
. El tiempo de ejecución enviará initialize
primero a todas las superclases de una clase (si las superclases aún no se han enviado initialize
).
Ejemplo:
@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;
}
Este programa imprime dos líneas de salida:
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
Dado que el sistema envía el initialize
método con pereza, una clase no recibirá el mensaje a menos que su programa realmente envíe mensajes a la clase (o una subclase, o instancias de la clase o subclases). Y para cuando lo reciba initialize
, todas las clases en su proceso ya deberían haber recibido load
(si corresponde).
La forma canónica de implementar initialize
es esta:
@implementation Someclass
+ (void)initialize {
if (self == [Someclass class]) {
// do whatever
}
}
El objetivo de este patrón es evitar Someclass
reinicializarse cuando tiene una subclase que no se implementa initialize
.
El motor de ejecución envía el initialize
mensaje en la _class_initialize
función en formato objc-initialize.mm
. Puede ver que utiliza objc_msgSend
para enviarlo, que es la función normal de envío de mensajes.
Otras lecturas
Consulte las preguntas y respuestas del viernes de Mike Ash sobre este tema.
+load
se envía por separado para categorías; es decir, cada categoría de una clase puede contener su propio+load
método.initialize
se invocará correctamente mediante unload
método, si es necesario, debido a laload
referencia a la entidad no inicializada. ¡Esto puede (extrañamente, pero sensiblemente) llevar ainitialize
correr antesload
! Eso es lo que he observado, de todos modos. Esto parece ser contrario a "Y para cuando lo recibainitialize
, todas las clases en su proceso ya deberían haber recibidoload
(si corresponde)".load
primero. A continuación, puede recibirinitialize
mientrasload
aún se está ejecutando.Lo que significa es no anular
+initialize
en una categoría, probablemente romperá algo.+load
se llama una vez por clase o categoría que implementa+load
, tan pronto como se carga esa clase o categoría. Cuando dice "vinculado estáticamente" significa compilado en el binario de su aplicación. Los+load
métodos de las clases así compiladas se ejecutarán cuando se inicie la aplicación, probablemente antes de que entremain()
. Cuando dice "cargado dinámicamente", significa cargado a través de paquetes de complementos o una llamada adlopen()
. Si está en iOS, puede ignorar ese caso.+initialize
se llama la primera vez que se envía un mensaje a la clase, justo antes de que maneje ese mensaje. Esto (obviamente) solo ocurre una vez. Si anula+initialize
en una categoría, sucederá una de estas tres cosas:Esta es la razón por la que nunca debe anular
+initialize
una categoría; de hecho, es bastante peligroso intentar reemplazar cualquier método en una categoría porque nunca está seguro de qué está reemplazando o si su propio reemplazo será reemplazado por otra categoría.Por cierto, otro tema a considerar
+initialize
es que si alguien te subclasifica, potencialmente te llamarán una vez para tu clase y una vez para cada subclase. Si está haciendo algo como configurarstatic
variables, querrá protegerse contra eso: ya sea condispatch_once()
o mediante pruebasself == [MyClass class]
.fuente