Must an Objective-C class have exactly one designated initializer? - objective-c

I found some info of the designated initializer in this Apple's docs, but what I don't understand is, must each class have one and only one designated initializer?
For example, what if class A has initL, initM, initN, while class B inherits from class A and has initX, initY, initZ. Is there a rule that says we can't have initX call [super initL], and initY call [super initM], and initZ call [super initN]?
That is, instead of all "secondary initializers" call the designated initializer, and then each designated initializer will call the [super initFoo] where initFoo is the superclass's designated initializer, can't we just have 3 primary initializers, and each one caller its corresponding superclass's 3 primary initializers? (and say, these all inherit from NSObject and just call self = [super init].)

No, an obj-c class may have multiple designated initializers. The most common example of this is -initWithCoder: vs -init. The former is used when unarchiving an object, and the latter is used for all other initialization.
That said, it's generally good practice to only have one designated initializer outside of -initWithCoder:. This helps to prevent code duplication and makes it obvious which method a subclass has to override if they want to be invoked for all initializations. But if you have a good case for needing 3 distinct designated initializers, then there's nothing stopping you from doing it. Just be sure to document it properly.

Designated initializers are a concept that helps to prevent recursive calls and omitted important base class initialization. It is possible to not follow the designated initializer rules and still build a working class hierarchy.
In fact there are patterns in Cocoa that deviate from pure designated initializes: NSCoding for example requires to initialze objects using initWithCoder: but you can still initialize objects from code using the other initializers.

Related

Must you override init when you create a new designated initializer?

I have read up quite a bit on designated and convenience initializers in objective-c and feel I have a good understanding of how and why they are used. I just have a couple of outstanding questions, well one really.
Say you have a class that inherits directly from NSObject and you create a designated initializer for it.
I realise you can now mark your designated initializer using NS_DESIGNATED_INITIALIZER but I am wondering if that means it forces you to separately, within the same class, first override init with a call to your designated initializers?
Secondly, if you weren't to use this macro, what might the implications be of NOT overriding init explicitly with the call to the designated initializer? I realise the compiler would not know which was the designated initializer, so could anything bad come of this, and is it just good practice to also override init when you create a new designated initalizer for your class?
EDIT: I just want to add further clarification on this question. I have seen examples of good coders not adding a distinct method to override init despite having added a designated initialiser of their own making.
When I ask if you should override init I mean in addition to creating your own specific designated initialiser which of course should call its superclass's init method. I have seen a good coder not do this and so I wondered why you would therefore do it.
you can now mark your designated initializer using NS_DESIGNATED_INITIALIZER but I am wondering if that means it forces you to separately, within the same class, first override init with a call to your designated initializer?
Calling any initializer in self will bypass the warning. When I try to call a super.init method I do get the following warning.
Convenience initializer missing a 'self' call to another initializer.
what might the implications be of NOT overriding init explicitly with the call to the designated initializer?
This would mean your object might be missing some important information that it requires to function, or that the app assumes the object always has, and if it doesn't have it, it could cause errors or a crash in your app.
Here is a good explanation of how it works:
http://timekl.com/blog/2014/12/09/objective-cs-designated-secret/

How to mark designated initializers of super class "invalid" in Objective-C?

From the Adapting Modern Objective-C document:
If a class provides one or more designated initializers, it must implement all of the designated initializers of its superclass.
That means if I have a subclass of NSObject that has its own designated initializer, say
- (instancetype)initWithImage:(UIImage*)image NS_DESIGNATED_INITALIZER;
then I also need to provide an implementation of NSObjects -init. What should I do to mark the -init initializer as "invalid", i.e., nobody should call it but use -initWithImage: instead? What's best practice here?
Edit
I tried the techniques described here.
However, when I mark the superclass -init method as unavailable in the interface, the compiler still tells me that I need to overwrite the initializer of the superclass.
When I try the other techniques, i.e., raising an exception or calling -doesNotRecognizeSelector: inside of -init, I get an error stating that I need to call one of my designated initializers.

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:
Objective-C: init vs initialize
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];.
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.
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];.

Objective-c using parent properties in init method

I read that using properties in init method is considered as bad practice. But should I use parent class properites ?
For example
-(id) init
{
if (self = [super init])
{
self.parentProp = someVal; // (1)
parentProp = someVal; // (2)
}
return self;
}
What is prefered (1 or 2) and why? Thanks!
After you've called super's init method, and it has returned, the superclass's part of your object is initialized and ready for use. It's normal and expected that you use its property accessors after that. For example. If you make a subclass of UIViewController, it's normal to then set your (inherited) title property, or modify your navigationItem, in your init method.
That said, you can break this behavior. If you've overridden one of your superclass's methods (including one of its accessors methods), and then you call that method in your init method, it's up to you to be sure your overridden method will behave properly before your object is fully initialized.
More subtly, maybe you're overridden a superclass method, and then you call a different superclass method that you haven't overridden. Well, what if the method you call turns around and calls the method you have overridden? You need to be aware of this possibility too.
All that said, I reiterate that it's perfectly normal to use your superclass's property accessors to customize it after you have initialized it by calling one of its init methods.
To answer your question - neither of them.
(2) is not a property access, but direct instance variable access. It depends on the class hierarchy design, but in general I would strongly discourage from using ivars in non-private interfaces - for details, see this answer to related question
In general, you should not use any of the class public methods (including properties access) in the class initializer (and in the dealloc for that matter) - if you class hierarchy doesn't prohibit subclassing explicitly. Because if you do - the subclasses overriding these methods (or properties accessors) will get them called while being in invalid state (not yet initialized or already dealloc'ed).
While I've encountered a number of problems caused by pt.2 in general it seems to be a common practice to ignore it (i.e. to use self/parent class properties in initializer). So I would say it's up to you. Either write more code for explicit setup outside of your classes initializers and feel confident that you would never encounter this problem. Or have probably more simple/short initialization and easier usage of your class but stay aware of that problem.

Is it necessary to implement all initializers for NSTextFieldCell subclass?

As per the docs, I have created a custom subclass of NSTextFieldCell with the sole purpose of overriding the method setUpFieldEditorAttributes:.
The docs don't mention any initializers for NSTextFieldCell's or its super class NSActionCell, but the documentation for NSCell explicitly mentions:
When subclassing NSCell you must implement all of the designated
initializers. Those methods are: init, initWithCoder:, initTextCell:,
and initImageCell:.
Now, since I don't really do anything in the custom subclass, except override an explicit override point in the Cocoa class, is there really any point in creating 4(!) initializers which only call their super class implementations?
Due to the verbose nature of the init... methods in Objective-C, this would quintuple the lines of code in the implementation, the number of comments to write (and read, for people into that sort of thing) and generally maintain.
I know that I could just have created those methods in the time it took me to write this, but I genuinely wonder if the existence of a method that just calls super makes any difference?
Edited to add:
I misread the NSTextFieldCell docs, which also state that all of the designated initializers must be implemented, but the question still stands - do 3 (init somehow disappeared as a designated initializer down the inheritance chain) initializers that just call super really make any difference?
In Apple's "DragNDropOutlineView" sample code, they implement "ImageAndTextCell", a custom subclass of NSTextFieldCell.
In it, the only initializer they override is
-(id) init
NSTextFieldCell apparently handles the others for you.
And, as you state, overriding a superclass's methods simply to call the superclass is a waste of time, as the runtime will do that if you don't override it anyway.