Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementation of -init vs. +initialize

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

like image 406
makaed Avatar asked Jan 18 '14 05:01

makaed


People also ask

Is implementing an interface inheritance?

Implementing an interface is indeed inheritance, and inheriting one interface from another is inheritance.

How do you implement an interface?

The implements keyword is used to implement an interface . The interface keyword is used to declare a special type of class that only contains abstract methods. To access the interface methods, the interface must be "implemented" (kinda like inherited) by another class with the implements keyword (instead of extends ).

What is implementation in Java?

Implementations are the data objects used to store collections, which implement the interfaces described in the Interfaces lesson. The Java Collections FrameworkJava Collections FrameworkA collection is an object that represents a group of objects (such as the classic Vector class). A collections framework is a unified architecture for representing and manipulating collections, enabling collections to be manipulated independently of implementation details.https://docs.oracle.com › docs › guides › collections › overviewCollections Framework Overview provides several general-purpose implementations of the core interfaces: For the Set interface, HashSet is the most commonly used implementation.


2 Answers

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.

like image 189
Greg Parker Avatar answered Nov 21 '22 04:11

Greg Parker


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

like image 30
Ken Thomases Avatar answered Nov 21 '22 05:11

Ken Thomases