Todd Ditchendorf
Todd Ditchendorf

Reputation: 11337

Should +initialize/+load always start with an: if (self == [MyClass class]) guard?

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.

Upvotes: 10

Views: 1745

Answers (3)

Lings
Lings

Reputation: 124

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.

Upvotes: 1

Matt Gallagher
Matt Gallagher

Reputation: 14968

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];
    }
}

Upvotes: 4

James Eichele
James Eichele

Reputation: 119164

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.

Upvotes: 5

Related Questions