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?
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.
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.
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.
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.
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