Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

+ (void) initialize not called (Objective C)

My method + (void) initialized is not called and I'm very new in Objective C. The code is in the book iPhone Game Development and I have to call the method explicitly to work. The code in the .m file is that:

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

But in the .h file a external declaration of the variable is made:

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

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

I tried everything (remove the external, put static in the .m declaration) and always get compilation errors. The code above compiles but the method initialize is never called (putted a breakpoint to see that).

Some clue?

like image 415
reinaldoluckman Avatar asked Feb 01 '10 16:02

reinaldoluckman


2 Answers

+initialize is not called until you send some message to an instance of the class. Did you send a message?

One possible problem might be that you sent a message to g_ResManager from another portion of your code? That won't work, because:

  1. g_ResManager is nil at the launch time.
  2. You send a message to g_ResManager, which is nil.
  3. What Objective-C runtime counts as "sending a message to a class" is not what it looks syntactically in the source code, but the real object and the message sent.
  4. So, in this case, nil gets the message, nil is not an instance of ResourceManager, so +initialize is not called either.

I would change your code as follows: first, in .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

and then in .h, I would just have

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

Then, you can always use [ResourceManager sharedResourceManager].

In fact, as Rob says in the comment, you can totally do away with +initialize in this case. Change .m to something like

@implementation ResourceManager

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

@end

This is the idiom I always use personally. But I warn you this is not completely thread safe! It should be OK as long as you call [ResourceManager sharedResourceManager] once before spawning threads, which I would almost always do anyway, but that's one thing to keep in mind. On the other hand, the version above using +initialize should be thread safe as is, thanks to the well-defined behavior of +initialize. See discussions in this blog post.

like image 64
Yuji Avatar answered Nov 13 '22 01:11

Yuji


From the documentation for NSObject:

The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

+initialize is only called before you send a class it's first message. Until you send a message to the class, the +initialize method is not called.

Eg:

If you call [[MyObject alloc] init];, +initialize is called on MyObject just before alloc is sent to MyObject.

like image 26
Jasarien Avatar answered Nov 13 '22 02:11

Jasarien