Skip to content

Instantly share code, notes, and snippets.

@steipete
Created January 15, 2015 12:01
Show Gist options
  • Save steipete/6d92218e055d195ebb05 to your computer and use it in GitHub Desktop.
Save steipete/6d92218e055d195ebb05 to your computer and use it in GitHub Desktop.
Ensure people call the right init
#define PSPDF_EMPTY_ASSERTING_INIT() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wobjc-designated-initializers\"") \
- (instancetype)init \
{ \
NSAssert(NO, @"Use the designated initializer to create an instance of this object."); \
return nil; \
} \
_Pragma("clang diagnostic pop")
@nesterenkodm
Copy link

why don't you use switf's approach to forward calls to designated initializer?

- (instancetype)init {
    return [self designatedInitWithParam:nil];
}

- (instancetype)designatedInitWithParam:(id)param {
    NSAssert(param != nil, @"Param can not be nil");
}

@vendruscolo
Copy link

If you have to do so, it means that you have the designated initializers chain broken.

On an app I have these view controller with different designated initializers:

@interface AVERegistrationViewController : UIViewController
- (instancetype)initForFirstRegistration:(BOOL)firstRegistration;
@end

@implementation AVERegistrationViewController

// This class designated initialiser
- (instancetype)initForFirstRegistration:(BOOL)firstRegistration {
    // call super's designated initializer
    self = [super initWithNibName:nil bundle:nil];
    if (!self) {
        return nil;
    }
    _firstRegistration = firstRegistration;
    return self;
}

// Override super's designated initializer calling this class' designated initializer
- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle {
    return [self initForFirstRegistration:YES];
}

@end

@interface AVEUserViewController : AVERegistrationViewController
- (instancetype)initWithUser:(AVEUser *)user;
@end

@implementation AVEStepViewController

// This class designated initialiser
- (instancetype)initWithUser:(AVEUser *)user {
    // call super's designated initializer
    self = [super initForFirstRegistration:YES];
    if (!self) {
        return nil;
    }
    _user = user;
    return self;
}

// Override super's designated initializer calling this class' designated initializer
- (instancetype)initForFirstRegistration:(BOOL)firstRegistration {
    return [self initWithUser:nil];
}

@end

If you do [[AVEUserViewController alloc] init] it does this:

  1. NSObject init is called;
  2. UIViewController ovverides init to call initWithNibName:bundle: (its designated initializer);
  3. AVERegistrationViewController overrides initWithNibName:bundle: to call initForFirstRegistration: (its designated initializer);
  4. AVEStepViewController overrides initForFirstRegistration: to call initWithUser:.

So, as long as every secondary initializer calls the designated one and each subclass overrides super's designated initializer to keep the links in the chain, you're safe and you can initialize objects as you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment