10

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).

Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
  • 2
    Can you add `NSLog(@"class=%@", NSStringFromClass([self class]));` and post the output. – trojanfoe Jan 01 '13 at 12:26
  • Helpful: http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/ – jrturton Jan 01 '13 at 12:29
  • 2
    @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 Jan 01 '13 at 12:35
  • 2
    The dynamically created subclass is discussed [here](http://www.mikeash.com/pyblog/friday-qa-2009-01-23.html), but that is not actual documentation. Hope it helps anyway, though. – Monolo Jan 01 '13 at 14:41
  • Set a breakpoint on the `+initialize` and paste all the backtraces in here. I'm interested to see the source. – bbum Jan 01 '13 at 18:18
  • 2
    @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 Jan 02 '13 at 01:52
  • Ahh -- OK -- that makes sense, then, and I should have put 2+2 together from the comment above. The dynamically created class is being initialized, but doesn't implement `+initialize`, thus calling yours multiple times. The runtime could add a mechanism for "create this class and treat it as already initialized", but that'd be an unnecessary addition of complexity given that `+initialize` may already be invoked multiple times for other reasons. – bbum Jan 02 '13 at 02:56
  • @bbum perhaps you'd like to add that as an answer so I can accept it? Personally I think they should make that change. They did it for `-dealloc` recently, why not this too. *shrug* `dispatch_once()` works fine. I think the documentation should be clearer though. – Abhi Beckert Jan 02 '13 at 06:28

3 Answers3

25

+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 ...
      });
}
Mike Abdullah
  • 14,933
  • 2
  • 50
  • 75
bbum
  • 162,346
  • 23
  • 271
  • 359
3

+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?

Monolo
  • 18,205
  • 17
  • 69
  • 103
0

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