10

When implementing an +initialize or +load method in one of your Objective-C classes, should you always start with this kind of guard?:

@implementation MyClass

+ (void)initialize {
    if (self == [MyClass class]) {
        ...
    }
}

...
@end

Seems like code in +load and +initialize usually only wants to be executed once. So this would help avoid dupe execution when subclasses load/initialize.

I guess I'm just wanting some reinforcement from some ObjC wizards that this is necessary/common practice...

What's the common wisdom on this? would you recommend always doing this?

Is your advice the same for both +load and +initialize, or is there a difference in they way they should be handled?

thanks.

Chris Hanson
  • 54,380
  • 8
  • 73
  • 102
Todd Ditchendorf
  • 11,217
  • 14
  • 69
  • 123

3 Answers3

5

The quick answer is: No.

An in-depth discussion of this matter can be found on the Apple developer mailing list.

The gist of it is that:

  1. The runtime will actually call +initialize on super classes before it is called on subclasses.
  2. If you do include the guard, subclasses of your class that have their own +initialize method will not trigger dependent KVO notifications.

For an example of point #2, be sure to read this post in the thread mentioned above.

e.James
  • 116,942
  • 41
  • 177
  • 214
  • If you're targeting Leopard you shouldn't be using +setKeys: triggerChangeNotificationsForDependentKey: but should instead be implementing +(NSSet *)keyPathsForValuesAffecting, and that takes care of the second point. – Ashley Clark Nov 28 '08 at 04:24
  • 2
    The vast majority of things that are done in `+initialize` should be done only once and only for the class that implemented it, not subclasses. The old KVO dependent keys mechanism is an important exception to note and it should be done outside of such a guard, but the guard should be used in most other cases. The cited email thread is mostly about whether `+initialize` should invoke `super`, which it should not. – Ken Thomases Jan 18 '14 at 05:52
4

Yes, you should do this in your intialize and load methods if you are initializing globals that should only be initialized once.

That said, there are a number of cases where you may avoid it...

You shouldn't wrap with this conditional if the work needs to be performed on every inheritant of every class:

  • For example, adding all inherited class names for each class to a set.
  • edited addition: or you're establishing KVO dependencies (as mentioned by eJames)

There are also situations where you just needn't bother:

  • If the actions you perform are idempotent (don't change values if repeated)
  • The class is "sealed" (has no descendants by design)

The "idempotent" part is relevant. An initializer should just be setting the initial state (which should be the same each time). In a good initializer, repetition shouldn't matter. Although I suppose that if you forget to wrap the method in the conditional when it does matter, this might be annoying.

edited addition: A different approach, that properly reflects any initialize-only-once requirements would be to test if your properties to initialize are initialized already. i.e.

id myGlobalObject = nil;

+(void)initialize
{
    if (myGlobalObject == nil)
    {
        myGlobalObject = [[MyGlobalClass alloc] init];
    }
}
Matt Gallagher
  • 14,858
  • 2
  • 41
  • 43
  • I prefer this answer, as it makes it clear (in the example) that a guard often _is_ necessary, but that the type of test may matter. (Although I think that if you are depending on `initialize()` not being overridden, in order to perform some action when a subclass loads, that's probably something that doesn't belong in `initialize()`.) – big_m Oct 03 '11 at 15:41
1

YES!!!!

Because the initialize method of a class may be invoked many times. e.g. when you implement initialize in parent class, and don't implement in sub class, then you call sub class first, the initialize of parent will invoked twice.

@implementation BaseClass

+ (void)initialize
{
    NSLog(@"BaseClass initialize self=%@, class=%@", self, [BaseClass class]);
}

@end

@interface SubClass : BaseClass
@end

@implementation SubClass

// don't implement the initialize method

@end

==================

now when you call SubClass first, just like

[SNSBaseSubLogic alloc]

look the debug console, output:

BaseClass initialize self=BaseClass, class=BaseClass
BaseClass initialize self=SubClass, class=BaseClass

so, you must use

+ (void)initialize
{
   if (self == [BaseClass class]) {
      NSLog(@"BaseClass initialize self=%@, class=%@", self, [BaseClass class]);
   }
}

to ensure the method body execute once.

Lings
  • 124
  • 1
  • 8