How to use NS_DESIGNATED_INITIALIZER and override init?

Ok, this is admittedly a slightly better question, but I want everything to be correct, so hopefully someone can enlighten me:

The script is pretty standard, but it has one twist: I have a class in the structure that I am writing that inherits directly from NSObject . It has a designated initializer with many arguments, most of which are nonnull . Since this is part of the structure, I explicitly use the NS_DESIGNATED_INITIALIZER macro (which I do not always do in small personal applications). The problem is that this causes Xcode to warn me of overriding init , that is, the initializer assigned by the superclass. But, in addition, it requires me to call my designated initalizer, which I cannot do, because I simply do not have enough meaningful default values ​​for its arguments. I really do not want to throw an exception in the "small" init , I would really like to return nil .

To get rid of the warning, I added init as the second designated initializer in my class extension, for example:

 @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 as I wanted. This means that my documentation (I use appledoc) and the Xcode extension of the extension code will not tell someone using my framework that init is actually also a designated initializer (so they will not accidentally use it), but it is still there (for example, in unit tests this may come in handy).

My question is: are there any dangers for this, besides the fact that someone really uses it in production, joyfully sending messages to zero after that, not understanding? Will this be one of the few cases where it is preferable to throw an exception in init?

+5
source share
1 answer

Instead of just returning nil from init (and perhaps adding a comment saying that you shouldn't call it) - you should mark it as unavailable.

Not only does this reject a warning that you are not overriding the designated NSObject initializer - it will also generate a compile-time error if someone tries to call init instead of the assigned initializer.

To do this, you can use the NS_UNAVAILABLE macro or use the inaccessible __attribute__ , as shown by this answer . The advantage of using __attribute__ is that you can specify the message that the compiler will present to the user.

For instance:

 @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 
+7
source

Source: https://habr.com/ru/post/1246952/


All Articles