Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 460
Todd Ditchendorf Avatar asked Nov 27 '08 21:11

Todd Ditchendorf


3 Answers

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.

like image 167
e.James Avatar answered Nov 14 '22 17:11

e.James


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];
    }
}
like image 4
Matt Gallagher Avatar answered Nov 14 '22 17:11

Matt Gallagher


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.

like image 1
Lings Avatar answered Nov 14 '22 18:11

Lings