10
votes

I have a +initialize method that is being called multiple times, and I don't understand why.

According to the documentation, it will be called once for every class (and subclasses as well),

This is the code I'm using:

@interface MyClass : NSObject

@end

static NSArray *myStaticArray;

@implementation MyClass

+ (void)initialize
{
  myStaticArray = [NSArray array];
}

@end

(obviously there is other code, but that's the relevant part).

There are no subclasses of MyClass. It doesn't do anything fancy. The +initialize gets called once, when my application is launched (NSApplication's delegate tells it to fill myStaticArray with data from the disk). And then +initialize is called a second time, the first time a user selects a menu item that relates to this class.

I've simply added dispatch_once() around my initialize code and this obviously fixes my problem. But I don't understand what is going on? Why is it called more than once when there are no subclasses?

This is the stack trace the first time +initialize is called:

+[MyClass initialize]
_class_initialize
objc_msgSend
-[MyAppDelegate applicationDidBecomeActive:]
_CFXNotificationPost
NSApplicationMain
main
start

And here is the second call:

+[MyClass initialize]
_class_initialize
NSApplicationMain
main
start

As you can see, my code does not appear to trigger the second call to +initialize (nothing in the stack trace). It occurs immediately after I display a window that presents the contents of the static array cleared by +initialize (the window shows the array contents, but immediately after that the array is empty).

3
Can you add NSLog(@"class=%@", NSStringFromClass([self class])); and post the output.trojanfoe
@trojanfoe thanks, I should have tried that earlier. The second time is triggered by NSKVONotifying_MyClass which I had never heard of until now. It seems KVO automatically creates subclasses of some objects? I have an NSTableView displaying some objects of this class (via NSArrayController). Does anybody know more about this? I can't find much documentation.Abhi Beckert
The dynamically created subclass is discussed here, but that is not actual documentation. Hope it helps anyway, though.Monolo
@bbum the source is Key Value Observing. I have a table view bound to an array controller, which is bound to an array that contains objects of this class. There's nothing useful in the stack trace, it's inside NSApplicationMain() but doing an NSLog on self reveals the class name: NSKVONotifying_MyClass. And it gets called shortly after I present the window containing the table view (not as part of presenting it. After that).Abhi Beckert

3 Answers

25
votes

+initialize will be sent to each class the first time it is referenced (by message), including dynamically created classes. There is no protection in the runtime against triggering execution multiple times. If a subclass is initialized, but doesn't implement +initialize, whatever super up the chain will have theirs called again.

Orthogonally, automatic KVO is implemented by creating dynamically derived subclasses of the class of the observed instance. That subclass is +initialized just like any other class, thus triggering multiple executions of the parent class's +initialize.

The runtime could take measures to protect against this. However, since +initialize has always been documented as potentially being executed multiple times, that added complexity (it is surprisingly complex, given that KVO classes come and go quite frequently, potentially) isn't deemed worth the effort.

The current recommended pattern is:

+ (void) initialize
{
      static dispatch_once_t once;
      dispatch_once(&once, ^{
        ... one time initialization here ...
      });
}
3
votes

+initialize gets called for each class up the inheritance chain, so if you initialise two classes that share the same superclass (or a superclass and one of its subclasses), then the superclass' +initialize method will get called twice.

Could that be the reason?

0
votes

1.Runtime called +initialize method in super class before subclass

2.if subclass have no method then it will call parent class initialize method.

3.Use always chronicle way of initialize method +(void)initialize{

if(self==[Car Class]){

//initialize here your static var
}  
}

for clear understanding read this post +(void)initialize