Question

Can anyone explain why we need to include if (self == SomeClass class) inside the +initialize method?

I’ve found similar questions (listed below), but didn’t find any specific clarifications:

  1. Objective-C: init vs initialize
  2. Should +initialize/+load always start with an: if (self == [MyClass class]) guard?

Everyone says that if you don’t implement/override +initialize in Subclass, then it’s going to call the parent class twice.

Can anyone explain that part in particular, specifically why does it call the parent class twice?

Lastly, why doesn’t it happen when we implement +initialize in the class that inherits from NSObject, where we create a custom -init method and call self = [super init];.

Was it helpful?

Solution 2

-init and +initialize are completely different things. The first is for initializing instances; the second is for initializing classes.

The first time any given class is messaged, the runtime makes sure to invoke +initialize on it and its superclasses. The superclasses are initialized first because they need to be ready before any subclass can initialize itself.

So, the first that time YourSubclass is messaged, the runtime might do something like:

[NSObject initialize];
[YourClass initialize];
[YourSubclass initialize];

(Although it's very unlikely that this would be the first time that NSObject is messaged, so probably it doesn't need to be initialized at this point. It's just for illustration.)

If YourSubclass doesn't implement +initialize, then the [YourSubclass initialize] invocation shown above will actually call +[YourClass initialize]. That's just the normal inheritance mechanism at work. That will make the second time that +[YourClass initialize] has been called.

Since the work done in a +initialize method is usually the sort of thing that should only be done once, the guard if (self == [TheClassWhoseImplementationThisMethodIsPartOf class]) is necessary. Also, that work often assumes that self refers to the current class being written, so that's also a reason for the guard.

The second answer you cite notes an exception, which is the old-style mechanism for registering KVO dependent keys with the +setKeys:triggerChangeNotificationsForDependentKey: method. That method is specific to the actual class it's invoked on, not any subclasses. You should avoid it and use the more modern +keyPathsForValuesAffectingValueForKey: or +keyPathsForValuesAffecting<Key> methods. If you must use the old way, put that part outside of the guard. Also, subclasses of such a class must call through to super which is not normally done.

Update:

A +initialize method should not normally call through to super because the runtime has already initialized the superclass. If and only if the superclass is known to register dependent keys using the old mechanism, then any subclasses must call through to super.

The same worry does not exist in the case of -init because the runtime is not automatically calling the superclass init method for you before calling yours. Indeed, if your init method does not call through to super, then nothing will have initialized the superclass's "part" of the instance.

OTHER TIPS

Imagine you have a superclass that implements +initialize and a subclass that does not.

@interface SuperClass : NSObject @end
@implementation SuperClass
+(void)initialize {
    NSLog(@"This is class %@ running SuperClass +initialize", self);
}
@end

@interface SubClass : SuperClass @end
@implementation SubClass
// no +initialize implementation
@end

Use the superclass. This provokes a call to +[SuperClass initialize].

[SuperClass class];
=> This is class SuperClass running SuperClass +initialize

Now use the subclass. The runtime looks for an implementation of +initialize in SubClass and does not find anything. Then it looks for an inherited implementation in SuperClass and finds it. The inherited implementation gets called even though it was already called once on behalf of SuperClass itself:

[SubClass class];
=> This is class SubClass running SuperClass +initialize

The guard allows you to perform work that must be run at most once. Any subsequent calls to +initialize have a different class as self, so the guard can ignore them.

The questions you cite have good accepted answers. To summarize, +initialize is called by the runtime on every class, so for a superclass with N subclasses, it will get called N+1 times on the superclass (once directly and once for each subclass that inherits it). Same thing if a subclass overrides it and calls super.

You can defend against this by asking at the superclass level, "is this my direct initialization by the system, and not 'super' being inherited or called by my subclass?"

if (self == [ThisSuperclass self]) {}

-init is used to initialize instances of classes and isn't invoked by the runtime by default like +initialize. Instances inherit their implementations of -init, can override the inherited implementation, and can also enjoy the benefit of the inherited implementation by calling [super init];.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top