How to mark designated initializers of super class "invalid" in Objective-C? - 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.

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/

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

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.

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.

How do I know which initializer is the designated initializer?

How do I know which initializer is the designated initializer for ANY class?
I'm guessing that it is the one that takes the most parameters, but there could be times where this is not correct.
omz's answer can be stated more firmly: The documentation for a framework class will specify which is the designated initializer. It's necessary to know what the designated initializer is in order to write subclasses that behave properly. The subclass's D.I. must call up to the superclass's D.I. in order to be sure that it is properly initialized.
Your guess about the greatest number of parameters is well-founded, however. Apple actually states that it is often the D.I. in a few places.
Cocoa Core Competencies
The initializer of a class that takes the full complement of initialization parameters is usually the designated initializer.
Cocoa Fundamentals
Some subclasses provide convenience initializers that supply default values to an initializer that takes the full complement of initialization parameters. This initializer is usually the designated initializer, the most important initializer of a class.
You can't really know this without looking at either the source code (impossible for system frameworks) or the documentation (potentially of a superclass).
From BigNerdRanch iOS programming book:
*The designated initializer calls the superclass’s designated initializer (directly or indirectly) before doing anything else.
*Any other initializers call the class’s designated initializer (directly or indirectly).
*If a class declares a designated initializer that is different from its superclass, the superclass’s designated initializer must be overridden to call the new designated initializer (directly or indirectly).
Conway, Joe; Hillegass, Aaron; Keur, Christian (2014-02-14). iOS Programming: The Big Nerd Ranch Guide (4th Edition) (Big Nerd Ranch Guides) (Kindle Locations 1906-1910). Pearson Education. Kindle Edition.