iOS Designated Initializers : Using NS_DESIGNATED_INITIALIZER

The use of NS_DESIGNATED_INITIALIZER is nicely explained in http://useyourloaf.com/blog/2014/08/19/xcode-6-objective-c-modernization.html:

The designated initializer guarantees the object is fully initialised by sending an initialization message to the superclass. The implementation detail becomes important to a user of the class when they subclass it. The rules for designated initializers in detail:

  • A designated initializer must call (via super) a designated initializer of the superclass. Where NSObject is the superclass this is just [super init].
  • Any convenience initializer must call another initializer in the class - which eventually leads to a designated initializer.
  • A class with designated initializers must implement all of the designated initializers of the superclass.

As an example, if your interface is

@interface MyClass : NSObject
@property(copy, nonatomic) NSString *name;
-(instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
-(instancetype)init;
@end

then the compiler checks if the (convenience) initializer init calls the (designated) initializer initWithName:, so this would cause a warning:

-(instancetype)init
{
    self = [super init];
    return self;
}

and this would be OK:

-(instancetype)init
{
    self = [self initWithName:@""];
    return self;
}

In Swift the rules about designated and convenience initializers are even more strict, and if you mix Objective-C and Swift code, marking the designated Objective-C initializers helps the compiler to enforce the rules.

For example, this Swift subclass would cause an compiler error:

class SwClass: MyClass {
    var foo : String
    init(foo : String) {
        self.foo = foo
        super.init()
    }
}

and this would be OK:

class SwClass: MyClass {
    var foo : String
    init(foo : String) {
        self.foo = foo
        super.init(name: "")
    }
}

My most common way to do this:

@interface Person : NSObject

- (nullable instancetype)initWithName:(nonnull NSString *)name NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE;

@property (nonatomic, nonnull) NSString *name;

@end

And implementation

@implementation Person

- (instancetype)initWithName:(NSString *)name
{
    self = [super init];
    if (self) {
        self.name = name;
    }
    return self;
}

@end

In this case you should not override NS_DESIGNATED_INITIALIZER of your superclass method (NSObject's init: in this case) - we used NS_UNAVAILABLE to mark this method as unneeded. Or you can override it to call your designated initializer with default parameters.


Designated initializers define how we structure our initializers when subclassing; they are the “canonical initializer” for your class. It guaranteed to be reliable regardless of which designated initializer in the superclass chain you call, and will always go from furthest ancestor to furthest descendant.

A designated initializer does not define what initializer you should use when creating an object. It's very explained in https://blog.twitter.com/2014/how-to-objective-c-initializer-patterns.