Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning: Method override for designated initializer

I programmatically create several tables and the code has worked fine for years. It did not generate any warnings two weeks ago when I last ran it. I’ve since updated to iOS 8.3 and I now get three warnings for each UITableViewController.

Method override for the designated initializer of the superclass '-initWithStyle:' not found.

Method override for the designated initializer of the superclass '-initWithCoder:' not found.

Method override for the designated initializer of the superclass '-initWithNibName:bundle:' not found.

The code to initialize the table is similar for all of my tables:

- (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context 
                 withScoreKeeper:(ScoreKeeper *)scorer 
                    withWordList:(WordList *)wordlist {

    self = [super initWithStyle:UITableViewStyleGrouped];

    if (self) {
        _mObjContext = context;
        _scoreKeeper = scorer;
        _wordList = wordlist;
    }
    return self;
}

and the .h looks like this:

@interface SettingsTableViewController : UITableViewController {
    UIPopoverController *popover;

}
    - (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context 
                     withScoreKeeper:(ScoreKeeper *)scorer 
                        withWordList:(WordList *)wordlist NS_DESIGNATED_INITIALIZER;

I thought that I was overriding a designated initializer by invoking self = [super initWithStyle:UITableViewStyleGrouped];, but I guess the compiler now has other ideas.

So how do I override the designated initializer?

like image 757
JScarry Avatar asked Apr 14 '15 23:04

JScarry


3 Answers

To forbid superclass NS_DESIGNATED_INITIALIZER

You can describe them as unavailable and implement them with exceptions.

For your example, in SettingsTableViewController.h:

@interface SettingsTableViewController : UITableViewController
- (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_UNAVAILABLE;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;

- (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context 
                 withScoreKeeper:(ScoreKeeper *)scorer 
                    withWordList:(WordList *)wordlist NS_DESIGNATED_INITIALIZER;

//...your class interface here

@end

In SettingsTableViewController.m:

@interface SettingsTableViewController ()
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end

@implementation SettingsTableViewController
- (instancetype)initWithStyle:(UITableViewStyle)style { @throw nil; }
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil { @throw nil; }
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { @throw nil; }

//...your class implementation here

@end

You can also add NSAssert(NO, nil); before @throw nil; to know the file and line number of the exception in the logs.

To allow superclass NS_DESIGNATED_INITIALIZER

You have to implement them explicitly. But nice thing to do would be to remove the public superfluous NS_DESIGNATED_INITIALIZER macro for your potential subclasses, and only reintroduce-it privately.

For your example, in SettingsTableViewController.h:

@interface SettingsTableViewController : UITableViewController
- (instancetype)initWithStyle:(UITableViewStyle)style;
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;

- (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context 
                 withScoreKeeper:(ScoreKeeper *)scorer 
                    withWordList:(WordList *)wordlist NS_DESIGNATED_INITIALIZER;

//...your class interface here

@end

In SettingsTableViewController.m:

@interface SettingsTableViewController ()
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end

@implementation SettingsTableViewController
- (instancetype)initWithStyle:(UITableViewStyle)style { return [super initWithStyle:style]; }
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil { return [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; }
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { return [super initWithCoder:aDecoder]; }

//...your class implementation here

@end

In both cases, (allowing or forbidding), no need to change the rest of your implementation.

like image 86
Cœur Avatar answered Oct 17 '22 05:10

Cœur


You've declared initInManagedObjectContext:withScoreKeeper:withWordList: to be the one-and-only designated initializer for SettingsTableViewController. That means that all other initializers must call it. But you inherited initWithStyle:, etc., and those those don't call your designated initializer. That means that if your object is created by calling initWithStyle: (or more importantly, initWithCoder:), it won't be properly initialized. Clang is warning you about that.

It appears you solved it by removing NS_DESIGNATED_INITIALIZER, and that's pretty common among Cocoa devs. I haven't seen many devs who routinely use that macro. But it leaves the potential bug. The more complete solution would be to override the superclass's designated initializers and implement them as calls to [self initInManagedObjectContext:withScoreKeeper:withWordList:], or implement them with NSAssert() to ensure that they correctly fail rather than just silently do the default thing (which might be right here, but might not be).

Note that Swift made this situation an error. It's just coming back into ObjC as a warning.

like image 14
Rob Napier Avatar answered Oct 17 '22 05:10

Rob Napier


Calling [super initWithStyle:UITableViewStyleGrouped] is not overriding the method in the superclass; you're just calling it.

I suspect that iOS 8.3 now shows you these warnings in Objective-C (we have been getting these warnings for a while in Swift).

I would just simply override those methods like this to get rid of the warnings:

- (instancetype)initWithStyle:(UITableViewStyle)style
{
    return [super initWithStyle:style];
}

It doesn't look like you need to do anything more in your case.

Update:

Try changing your convenience initializer to this:

- (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context 
                 withScoreKeeper:(ScoreKeeper *)scorer 
                    withWordList:(WordList *)wordlist {

    self = [self initWithStyle:UITableViewStyleGrouped]; // <-- self instead of super

    if (self) {
        _mObjContext = context;
        _scoreKeeper = scorer;
        _wordList = wordlist;
    }
    return self;
}

That method is a convenience initializer and should always call one of the designated initializers in the current class. That one can then call the superview's designated initializer.

like image 8
Thomas Müller Avatar answered Oct 17 '22 07:10

Thomas Müller