2010-02-01 9 views
10

Mi método + (void) initialized no se llama y soy muy nuevo en Objective C. El código está en el libro iPhone Game Development y tengo que llamar al método explícitamente a trabajar. El código en el archivo .m es que:+ (void) initialize no llamado (Objective C)

ResourceManager *g_ResManager; 

@implementation ResourceManager 

//initialize is called automatically before the class gets any other message, per from http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like 
+ (void) initialize 
{ 
    static BOOL initialized = NO; 
    if(!initialized) 
    { 
     initialized = YES; 
     g_ResManager = [[ResourceManager alloc] init]; 
    } 
} 

... 

@end 

Pero en el archivo .h se hace una declaración externa de la variable:

extern ResourceManager *g_ResManager; //paul <3's camel caps, hungarian notation, and underscores. 

@interface ResourceManager : NSObject { 
    ... 
} 
... 
@end 

He intentado todo (quitar la externa, poner en estático la declaración .m) y siempre obtiene errores de compilación. El código anterior compila, pero el método de inicialización nunca se llama (puso un punto de interrupción para ver eso).

¿Alguna idea?

Respuesta

14

+initialize no se llama hasta que envíe un mensaje a una instancia de la clase. ¿Has enviado un mensaje?

¿Un posible problema podría ser que haya enviado un mensaje al g_ResManager desde otra parte de su código? Eso no va a funcionar, porque:

  1. g_ResManager es nula en el momento del lanzamiento.
  2. Usted envía un mensaje a g_ResManager, que es nil.
  3. Lo que el tiempo de ejecución de Objective-C cuenta como "enviar un mensaje a una clase" no es lo que parece sintácticamente en el código fuente, sino el objeto real y el mensaje enviado.
  4. Por lo tanto, en este caso, nil recibe el mensaje, nil no es una instancia de ResourceManager, por lo que no se llama +initialize tampoco.

me iba a cambiar su código de la siguiente manera: en primer lugar, en .m,

static ResourceManager *g_ResManager; 

@implementation ResourceManager 

//initialize is called automatically before the class gets any other message 
+ (void) initialize 
{ 
    static BOOL initialized = NO; 
    if(!initialized) 
    { 
     initialized = YES; 
     g_ResManager = [[ResourceManager alloc] init]; 
    } 
} 
+(ResourceManager*)sharedResourceManager 
{ 
    return g_ResManager; 
} 
... 

@end 

y luego en.h, que sólo tendría

@interface ResourceManager:NSObject { 
... 
} 
+(ResourceManager*)sharedResourceManager 
@end 

Entonces, siempre se puede utilizar [ResourceManager sharedResourceManager].

De hecho, como dice Rob en el comentario, puede eliminar totalmente +initialize en este caso. Cambie .m a algo como

@implementation ResourceManager 

+(ResourceManager*)sharedResourceManager 
{ 
    static ResourceManager *g_ResManager=nil; 
    if(!g_ResManager){ 
     g_ResManager=[[ResourceManager alloc] init]; 
    } 
    return g_ResManager; 
} 
... 

@end 

Este es el modismo que uso siempre personalmente. ¡Pero te advierto que esto no es completamente seguro para hilos! Debería estar bien siempre que llame al [ResourceManager sharedResourceManager] una vez antes de los hilos de desove, lo que casi siempre haría de todos modos, pero eso es algo a tener en cuenta. Por otro lado, la versión anterior que usa +initialize debería ser segura para subprocesos, gracias al comportamiento bien definido de +initialize. Ver discusiones en this blog post.

+0

Esto es exactamente lo que estaba sucediendo. ¡¡¡Muchas gracias!!! Soy de Java y siempre obtengo una NullPointerException en esos casos. : D ¡Gracias de nuevo! – reinaldoluckman

+0

Es bueno saber que funcionó. De todos modos, solo muestra que hay tantos libros de programación de iPhone mal escritos por inexpertos. Ningún desarrollador de Obj-C en su sano juicio sería una variable de instancia externa global. Es una expresión idiomática hacer un '+ sharedFoo' que devuelve la instancia compartida. – Yuji

+0

Cuéntame sobre eso. Eso realmente tiene sentido. Use el patrón singleton y declare una variable externa. Pero debido a mi bajo nivel de conocimiento en Objective-C, me fui de esa manera. Ahora todo está bien. Ya no hay ninguna variable externa. Gracias de nuevo. – reinaldoluckman

6

De la documentación para NSObject:

El tiempo de ejecución envía inicializar a cada clase en un programa de exactamente una hora justo antes de la clase, o cualquier clase que herede de ella, se envía su primera mensaje desde dentro del programa. (Por lo tanto, nunca se puede invocar el método si no se utiliza la clase). El tiempo de ejecución envía el mensaje de inicialización a las clases de forma segura para subprocesos. Las superclases reciben este mensaje antes de sus subclases.

+initialize sólo se le llama antes de enviar una clase que es primer mensaje. Hasta que envíe un mensaje a la clase, no se llama al método +initialize.

Ej:

Si llama [[MyObject alloc] init];, +initialize se llama en MiObjeto justo antes alloc se envía a MiObjeto.

+0

Gracias por la respuesta también, Jasarien. – reinaldoluckman

0

Tenga en cuenta que cuando no asigna un objeto usted mismo, debe conservarlo. Ejemplo:

+ (NSPredicate *)somePredicate 
{ 
    static NSPredicate *predicate = nil; 
    if (!predicate) { 
     predicate = [NSPredicate predicateWithFormat:@"status == 2"]; 
     [predicate retain]; 
    } 
    return predicate; 
} 

De lo contrario, no sobrevive el tiempo suficiente para ser usado un par de veces (el puntero sigue siendo !nil, pero no válida más).

+0

Esto es cierto, pero no es una respuesta a la pregunta. – benzado

+0

De acuerdo, debería haber hecho este comentario. Lo siento. – Dirk

+0

@EricGoldberg Este método posee el valor retenido; está almacenado en una variable estática. Este es el código correcto. –