Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does registering a subclass with its superclass in +initialize present a chicken and egg issue? (objc)

Just reading an excerpt from this website.

Because +initialize runs lazily, it's obviously not a good place to put code to register a class that otherwise wouldn't get used. For example, NSValueTransformer or NSURLProtocol subclasses can't use +initialize to register themselves with their superclasses, because you set up a chicken-and-egg situation.

I understand that +initialize is run once per class when the first message is sent to that class. Also, if any of the subclasses do not implement their own +initialize, the +initialize method will be run again in the superclass.

I am just not 100% on why registering a subclass with its superclass in its own +initialize method would present a chicken and egg problem.

Is it because the superclass may have never had its +initialize invoked, and you are trying to register your subclass with its superclass in a method that depends on the superclass calling its +initialize first?

Just a little bit of further clarification would go a long way for me, thank you.

like image 350
NYC Tech Engineer Avatar asked Dec 10 '25 00:12

NYC Tech Engineer


2 Answers

Take the example of NSURLProtocol. The way it's used is that registered subclasses are asked, in turn, if they can handle a request. The first to answer yes gets an instance created and the request is handed off.

The initialize method is only called if a message is sent to the class. Since only registered subclasses are asked to handle a request, you can't register in initialize because it won't ever be invoked.

like image 144
Avi Avatar answered Dec 12 '25 12:12

Avi


Two extracts from the documentation on the initialize method:

The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

...

Because initialize is called in a thread-safe manner and the order of initialize being called on different classes is not guaranteed, it’s important to do the minimum amount of work necessary in initialize methods. Specifically, any code that takes locks that might be required by other classes in their initialize methods is liable to lead to deadlocks. Therefore you should not rely on initialize for complex initialization, and should instead limit it to straightforward, class local initialization.

The initialize message is sent to a class the first time the runtime encounters it, for example the first time you need to allocate that class or the first time you access its sharedInstance method (in case of a singleton), and it acquires some locks in order to guarantee the thread safety. If you make references to subclasses from within this method, you can get into a deadlock situation, as both the base class and the subclass will lock onto the same thing.

For example, let's consider the scenario of a superclass MyClass and one of it's children MySubclass:

@interface MyClass
@end

@interface MySubclass: MyClass
@end

@implementation MyClass

+ (void)initialize {
    [MySubclass doSomething];
}

When the runtime encounters the first usage of MyClass, it acquires a lock, and calls the class method initialize. Now, when executing the method it realises that this is also the first time it encounters MySubclass, and must also intialize it before the class can do some actual work. And what does this trigger? Yes, you've guessed, another call to +[MyClass initialize].

This how we end up in the chicken-egg situation, or to put it more technical - the deadlock, or the recursion. MyClass calls on MySubclass, this means that MySubclass needs to be initialized before MyClass is used. However MySubclass is a child of MyClass, so MyClass should be initialized first. So, which one the two should be first initialized?

like image 28
Cristik Avatar answered Dec 12 '25 12:12

Cristik