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:
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];
.
Implementing an interface is indeed inheritance, and inheriting one interface from another is inheritance.
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 ).
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.
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.
-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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With