我以编程方式创建了几个表,并且代码已经运行了好几年。两周前我上次运行时没有发出任何警告。我已经更新到iOS 8.3,现在每个UITableViewController都会收到三个警告。
超类的指定初始值设定项的方法覆盖 '-initWithStyle:'找不到。
超类的指定初始值设定项的方法覆盖 '-initWithCoder:'找不到。
超类的指定初始值设定项的方法覆盖 '-initWithNibName:bundle:'找不到。
初始化表的代码与我的所有表类似:
- (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context
withScoreKeeper:(ScoreKeeper *)scorer
withWordList:(WordList *)wordlist {
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
_mObjContext = context;
_scoreKeeper = scorer;
_wordList = wordlist;
}
return self;
}
和.h看起来像这样:
@interface SettingsTableViewController : UITableViewController {
UIPopoverController *popover;
}
- (instancetype)initInManagedObjectContext:(NSManagedObjectContext *)context
withScoreKeeper:(ScoreKeeper *)scorer
withWordList:(WordList *)wordlist NS_DESIGNATED_INITIALIZER;
我认为我通过调用self = [super initWithStyle:UITableViewStyleGrouped];来覆盖指定的初始化程序,但我想编译器现在有其他想法。
那么如何覆盖指定的初始值设定项?
答案 0 :(得分:52)
NS_DESIGNATED_INITIALIZER
您可以将它们描述为不可用,并在例外情况下实施它们。
对于您的示例,在 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
在 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
您还可以在NSAssert(NO, nil);
之前添加@throw nil;
,以便了解日志中的例外文件和行号。
NS_DESIGNATED_INITIALIZER
您必须明确地实施它们。但好的做法是删除潜在子类的公共多余的NS_DESIGNATED_INITIALIZER
宏,并且只能私下重新引入它。
对于您的示例,在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
在 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
在这两种情况下,(允许或禁止),无需更改其余的实施。
答案 1 :(得分:12)
您已将initInManagedObjectContext:withScoreKeeper:withWordList:
声明为SettingsTableViewController
的唯一指定初始值设定项。这意味着所有其他初始值设定项必须调用它。但是你继承了initWithStyle:
等等,而那些那些没有调用你指定的初始化程序。这意味着如果您的对象是通过调用initWithStyle:
(或更重要的是initWithCoder:
)创建的,则无法正确初始化它。 Clang正在警告你。
看起来你通过删除NS_DESIGNATED_INITIALIZER
来解决它,这在Cocoa开发中很常见。我没见过很多经常使用那个宏的开发者。但它留下了潜在的错误。更完整的解决方案是覆盖超类的指定初始化器并将它们实现为[self initInManagedObjectContext:withScoreKeeper:withWordList:]
的调用,或者使用NSAssert()
实现它们以确保它们正确失败而不是默默地执行默认操作(可能就在这里,但可能不是。)
请注意,Swift使这种情况出错。它刚刚回到ObjC作为警告。
答案 2 :(得分:8)
调用[super initWithStyle:UITableViewStyleGrouped]
不会覆盖超类中的方法;你只是打电话给它。
我怀疑iOS 8.3现在会在Objective-C中向您显示这些警告(我们已经在Swift中获得了一些警告)。
我只是简单地覆盖这样的方法来摆脱警告:
- (instancetype)initWithStyle:(UITableViewStyle)style
{
return [super initWithStyle:style];
}
在您的情况下,您似乎不需要做更多其他事情。
<强>更新强>
尝试将便利初始化程序更改为:
- (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;
}
该方法是一个便利初始化程序,应始终调用当前类中的一个指定初始值设定项。然后,那个人可以调用superview的指定初始化程序。
答案 3 :(得分:3)
万一有人想知道更多关于错误的信息,这就是我发现的。我不是程序员,所以部分可能是错误的。
当我第一次使用Xcode 5和Xcode 6打开我的项目时,我被提示&#34;转换为现代Objective-C语法&#34;。 (最新版本的Xcode将其置于编辑&gt;转换&gt;转换为现代Objective-C语法。)我允许Xcode转换所有内容并理解大部分更改。我理解NS_DESIGNATED_INITIALIZER是如何工作的,但不知道它的工作原理。由于我没有编译器错误,一切都像以前一样工作,我很快忘记了它。在最新版本的Xcode中,它们似乎已经更新了编译器,这就是触发我的警告信息的原因。
在Apple的笔记中:Adopting Modern Objective C
使用此宏会引入一些限制:
指定初始化程序的实现必须链接到a 指定的超类init方法(使用[super init ...]) 超类的初始化程序。
实现一个便利初始化器(初始化器不是 标记为至少具有类的指定初始值设定项 一个标记为指定初始化程序的初始值设定项必须委托给 另一个初始化器(使用[self init ...])。
如果一个类提供了一个或多个指定的初始值设定项,则必须 实现其超类的所有指定初始值设定项。
这就是我认为发生的事情。首先,我收到三条警告消息,因为UITableViewController有三个指定的初始化器。
> - (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
> - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
> - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
我违反了第三个限制,因为我没有实现超类的所有指定初始值设定项。
从.h文件中删除NS_DESIGNATED_INITIALIZER宏会使警告消失。
然后出现问题,我是否关心这些类没有指定的初始化器?可能不是。 首先,这些类中没有其他初始化器,因此我不会意外地调用错误的初始化器。其次,我不是训练的程序员,所以当我开始编写应用程序时,我使用了我习惯的程序编程风格。直到最近,我还没有将一个班级分类。所以我不会将这个子类化为子类,并且不会有任何子类需要担心。现在我对Objective C有了更多的了解,结果发现我写的每个类都是iOS类之一的子类,这实际上解释了我为什么会收到错误。
解决Rob的观点。我没有意识到我可以通过调用其超类上的方法来创建表视图对象。例如,此调用有效:
SettingsTableViewController *stvc = [[SettingsTableViewController alloc] initWithStyle: UITableViewStyleGrouped];
即使设置了NS_DESIGNATED_INITIALIZER,它也能正常工作。可能没有发送其他警告,因为编译器已经在抱怨没有调用指定的超级初始化器。
只要在表视图中调用的视图不需要传入的任何对象,一切都很好。如果在表中链接的视图确实需要其中一个对象,那么很明显应用程序崩溃了。
因为除了指定的初始化程序之外我永远不想调用任何东西,在Rob建议使用NSAssert()之后,我可以确保我不会调用我的超类的指定初始化程序,并使所有警告都消失了代码:
- (instancetype)initWithStyle:(UITableViewStyle)style {
NSAssert(NO, @"%@", @"Tried to implement with initWithStyle");
self = [self initInManagedObjectContext:nil withScoreKeeper:nil withWordList:nil];
return self;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
NSAssert(NO, @"%@", @"Tried to implement with initWithNibName");
self = [self initInManagedObjectContext:nil withScoreKeeper:nil withWordList:nil];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
NSAssert(NO, @"%@", @"Tried to implement with initWithCoder");
self = [self initInManagedObjectContext:nil withScoreKeeper:nil withWordList:nil];
return self;
}
当我尝试直接调用initWithStyle时,我在日志中收到此错误。
***断言失败 - [SettingsTableViewController initWithStyle:], /Users/jscarry/Words/Words/Settings/SettingsTableViewController.m:37
有用的链接: iOS Designated Initializers : Using NS_DESIGNATED_INITIALIZER
This文章详细介绍了它实施的原因。
答案 4 :(得分:3)
您可以使用@steipete分享的此macro。可以解决问题。
#define PSPDF_NOT_DESIGNATED_INITIALIZER() PSPDF_NOT_DESIGNATED_INITIALIZER_CUSTOM(init)
#define PSPDF_NOT_DESIGNATED_INITIALIZER_CUSTOM(initName) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wobjc-designated-initializers\"") \
- (instancetype)initName \
{ do { \
NSAssert2(NO, @"%@ is not the designated initializer for instances of %@.", NSStringFromSelector(_cmd), NSStringFromClass([self class])); \
return nil; \
} while (0); } \
_Pragma("clang diagnostic pop")
用法:
// IBLAirHomeActionsBarItem.h
@interface IBLAirHomeActionsBarItem : IBLBaseView
@property (nonatomic, copy, readonly) NSString *title;
@property (nonatomic, strong, readonly) UIImage *image;
- (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image NS_DESIGNATED_INITIALIZER;
@end
// IBLAirHomeActionsBarItem.m
@implementation IBLAirHomeActionsBarItem
PSPDF_NOT_DESIGNATED_INITIALIZER_CUSTOM(initWithFrame:(CGRect)frame)
PSPDF_NOT_DESIGNATED_INITIALIZER_CUSTOM(initWithCoder:(NSCoder *)aDecoder)
- (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image {
self = [super initWithFrame:CGRectZero];
if (self) {
}
return self;
}
@end
任何未指定的初始化程序调用都将引发断言错误。