For certain classes, I would like to explicitly call the +initialize
method when my program starts, rather than allowing the runtime system to call it implicitly at some nondeterministic point later when the class happens to first be used. Problem is, this isn't recommended.
Most of my classes have little to no work to do in initialization, so I can just let the runtime system do its thing for those, but at least one of my classes requires as much as 1 second to initialize on older devices, and I don't want things to stutter later when the program is up and running. (A good example of this would be sound effects — I don't want sudden delay the first time I try to play a sound.)
What are some ways to do this initialization at startup-time?
What I've done in the past is call the +initialize
method manually from main.c
, and made sure that every +initialize
method has a bool initialized
variable wrapped in a @synchronized
block to prevent accidental double-initialization. But now Xcode is warning me that +initialize
would be called twice. No surprise there, but I don't like ignoring warnings, so I'd rather fix the problem.
My next attempt (earlier today) was to define a +preinitialize
function that I call directly instead +initialize
, and to make sure I call +preinitialize
implicitly inside of +initialize
in case it is not called explicitly at startup. But the problem here is that something inside +preinitialize
is causing +initialize
to be called implicitly by the runtime system, which leads me to think that this is a very unwise approach.
So let's say I wanted to keep the actual initialization code inside +initialize
(where it's really intended to be) and just write a tiny dummy method called +preinitialize
that forces +initialize
to be called implicitly by the runtime system somehow? Is there a standard approach to this? In a unit test, I wrote...
+ (void) preinitialize
{
id dummy = [self alloc];
NSLog(@"Preinitialized: %i", !!dummy);
}
...but in the debugger, I did not observe +initialize
being called prior to +alloc
, indicating that +initialize
was not called implicitly by the runtime system inside of +preinitialize
.
I found a really simple solution, and posted it as an answer.
Answering my own question here. It turns out that the solution is embarrassingly simple.
I had been operating under the mistaken belief that +initialize
would not be called until the first instance method in a class is invoked. This is not so. It is called before the first instance method or class method is invoked (other than +load
, of course).
So the solution is simply to cause +initialize
to be invoked implicitly. There are multiple ways to do this. Two are discussed below.
In startup code, simply call some method (e.g., +class
) of the class you want to initialize at startup, and discard the return value:
(void)[MyClass class];
This is guaranteed by the Objective-C runtime system to call [MyClass initialize]
implicitly if it has not yet been called.
Create a +preinitialize
method with an empty body:
+ (void) preinitialize
{
// Simply by calling this function at startup, an implicit call to
// +initialize is generated.
}
Calling this function at startup implicitly invokes +initialize
:
[MyClass preinitialize]; // Implicitly invokes +initialize.
This +preinitialize
method serves no purpose other than to document the intention. Thus, it plays well with +initialize
and +deinitialize
and is fairly self-evident in the calling code. I write a +deinitialize
method for every class I write that has an +initialize
method. +deinitialize
is called from the shutdown code; +initialize
is called implicitly via +preinitialize
in the startup code. Super simple. Sometimes I also write a +reinitialize
method, but the need for this is rare.
I am now using this approach for all my class initializers. Instead of calling [MyClass initialize]
in the start up code, I am now calling [MyClass preinitialize]
. It's working great, and the call stack shown in the debugger confirms that +initialize
is being called exactly at the intended time and fully deterministically.