Okay, this is admittedly a bit of a best practice question, but I want to get it right, so hopefully someone can enlighten me:
Scenario is pretty standard, but has one twist:
I have a class in a framework I write that inherits directly from NSObject
. It has a designated initializer with quite a few arguments, most of which are nonnull
. Since the thing is part of a framework, I explicitly use the NS_DESIGNATED_INITIALIZER
macro (which I don't always do in smaller, personal apps).
The problem is that this leads to XCode warning me to also override init
, i.e. the superclass's designated initializer. But additionally it demands that I call my designated initalizer from it, which I can't do, because I simply lack meaningful defaults for its arguments.
I don't really want to throw an exception in the "small" init
, I'd much rather like to return nil
.
To get rid of the warning, I added init
as a second designated initalizer in my class's extension like so:
@interface MyClassName ()
// some other stuff not relevant`
-(nullable instancetype)init NS_DESIGNATED_INITIALIZER;
@end
Now I can safely just return nil;
in the overriden init
method like I wanted.
That means my documentation (I'm using appledoc) and by extension XCode's code completion won't tell someone using my framework that init
is actually also a designated initializer (so they won't accidentally use it), but it is still there (in unit tests, for example, this could come in handy).
My question is: Are there any dangers to this besides from someone actually using it in production, happily sending messages to nil afterwards without realizing? Would this be one of the few cases where throwing an exception in init would be preferable?
Rather than just returning nil
from init
(and maybe adding a comment saying you shouldn't call it) – you should mark it as unavailable.
Not only will this dismiss the warning about you not overriding NSObject
's designated initialiser – it will also generate a compile-time error if anyone tries to call init
instead of your designated initialiser.
To do this, you can either use the NS_UNAVAILABLE
macro, or use the unavailable __attribute__
as shown by this answer. The advantage of using the __attribute__
is you can specify a message that the compiler will present to the user.
For example:
@interface Foo : NSObject
-(instancetype) init __attribute__((unavailable("You cannot create a foo instance through init - please use initWithBar:")));
-(instancetype) initWithBar:(Bar*)bar NS_DESIGNATED_INITIALIZER;
@end
...
Foo* fooA = [[Foo alloc] init]; // ERROR: 'init' is unavailable: You cannot create a foo instance through init - please use initWithBar:
Foo* fooB = [[Foo alloc] initWithBar:[[Bar alloc] init]]; // No error
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