简体   繁体   English

警告:指定初始值设定项的方法覆盖

[英]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. 我已经更新到iOS 8.3,现在我为每个UITableViewController收到三个警告。

Method override for the designated initializer of the superclass '-initWithStyle:' not found. 超类'-initWithStyle:'的指定初始值设定项的方法覆盖未找到。

Method override for the designated initializer of the superclass '-initWithCoder:' not found. 超类'-initWithCoder的指定初始值设定项的方法覆盖:'未找到。

Method override for the designated initializer of the superclass '-initWithNibName:bundle:' not found. 超类“-initWithNibName:bundle:”的指定初始化程序的方法覆盖:未找到。

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: 而.h看起来像这样:

@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. 我以为我通过调用self = [super initWithStyle:UITableViewStyleGrouped];来覆盖指定的初始化程序,但我想编译器现在有其他想法。

So how do I override the designated initializer? 那么如何覆盖指定的初始化程序?

To forbid superclass NS_DESIGNATED_INITIALIZER 禁止超类NS_DESIGNATED_INITIALIZER

You can describe them as unavailable and implement them with exceptions. 您可以将它们描述为不可用,并使用例外实现它们。

For your example, in SettingsTableViewController.h : 对于您的示例,在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 : 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); 你也可以添加NSAssert(NO, nil); before @throw nil; @throw nil;之前@throw nil; to know the file and line number of the exception in the logs. 知道日志中的异常的文件和行号。

To allow superclass NS_DESIGNATED_INITIALIZER 允许超类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. 但是,好的做法是为潜在的子类删除公共多余的NS_DESIGNATED_INITIALIZER宏,并且只能私下重新引入它。

For your example, in SettingsTableViewController.h: 对于您的示例,在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 : 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 . 您已将initInManagedObjectContext:withScoreKeeper:withWordList:声明为SettingsTableViewController的唯一指定初始值SettingsTableViewController That means that all other initializers must call it. 这意味着所有其他初始值设定项必须调用它。 But you inherited initWithStyle: , etc., and those those don't call your designated initializer. 但是你继承了initWithStyle:等,那些那些没有调用你指定的初始化程序。 That means that if your object is created by calling initWithStyle: (or more importantly, initWithCoder: ), it won't be properly initialized. 这意味着如果您的对象是通过调用initWithStyle:或更重要的是, initWithCoder:创建的,那么它将无法正确初始化。 Clang is warning you about that. Clang正在警告你。

It appears you solved it by removing NS_DESIGNATED_INITIALIZER , and that's pretty common among Cocoa devs. 看来你通过删除NS_DESIGNATED_INITIALIZER来解决它,这在Cocoa开发中很常见。 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). 更完整的解决方案是覆盖超类的指定初始值并将其实现为[self initInManagedObjectContext:withScoreKeeper:withWordList:]调用,或者使用NSAssert()实现它们以确保它们正确失败而不是默默地执行默认操作(这可能就在这里,但可能不是)。

Note that Swift made this situation an error. 请注意,Swift使这种情况出错。 It's just coming back into ObjC as a warning. 它只是回到ObjC作为警告。

Calling [super initWithStyle:UITableViewStyleGrouped] is not overriding the method in the superclass; 调用[super initWithStyle:UITableViewStyleGrouped]不会覆盖超类中的方法; 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). 我怀疑iOS 8.3现在会在Objective-C中向您显示这些警告(我们已经在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. 那个人可以调用superview的指定初始化程序。

Just in case anyone wants to know more about the error, here's what I found out. 万一有人想知道更多关于错误的信息,这就是我发现的。 I am not a programmer, so parts could be mistaken. 我不是程序员,所以部分可能是错误的。

When I opened my project for the first time with Xcode 5 and Xcode 6, I was prompted to "Convert to Modern Objective-C Syntax". 当我第一次用Xcode 5和Xcode 6打开我的项目时,我被提示“转换为现代Objective-C语法”。 (The latest version of Xcode puts it under Edit > Convert > Convert to Modern Objective-C Syntax.) I allowed Xcode to convert everything and understood most of the changes. (最新版本的Xcode将其置于编辑>转换>转换为现代Objective-C语法。)我允许Xcode转换所有内容并理解大部分更改。 I understood how NS_DESIGNATED_INITIALIZER works but not why it works. 我理解NS_DESIGNATED_INITIALIZER是如何工作的,但不知道它的工作原理。 Since I had no compiler errors and everything worked as before, I promptly forgot about it. 由于我没有编译器错误,一切都像以前一样工作,我很快忘记了它。 In the latest version of Xcode, they appear to have updated the compiler and that's what triggered my warning message. 在最新版本的Xcode中,他们似乎更新了编译器,这就是触发我的警告信息的原因。

In Apple's notes: Adopting Modern Objective C 在Apple的笔记中: 采用现代目标C.

Using this macro introduces a few restrictions: 使用此宏引入了一些限制:

The implementation of a designated initializer must chain to a superclass init method (with [super init...]) that is a designated initializer for the superclass. 指定初始化程序的实现必须链接到超类init方法(使用[super init ...]),这是超类的指定初始化程序。

The implementation of a convenience initializer (an initializer not marked as a designated initializer within a class that has at least one initializer marked as a designated initializer) must delegate to another initializer (with [self init...]). 方便初始化程序(初始化程序未标记为具有至少一个标记为指定初始化程序的初始化程序的类中的指定初始化程序)的实现必须委托给另一个初始化程序(使用[self init ...])。

If a class provides one or more designated initializers, it must implement all of the designated initializers of its superclass. 如果类提供一个或多个指定的初始值设定项,则它必须实现其超类的所有指定初始值设定项。

Here's what I think happened. 这就是我认为发生的事情。 First, I got three warning messages because UITableViewController has three designated initializers. 首先,我收到三条警告消息,因为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;

I violated the third restriction because I did not implement all of the designated initializers of the superclass. 我违反了第三个限制,因为我没有实现超类的所有指定初始值设定项。

Removing the NS_DESIGNATED_INITIALIZER macro from the .h files made the warnings go away. 从.h文件中删除NS_DESIGNATED_INITIALIZER宏使警告消失。

The question then arises, Do I care that these classes have no designated initializers? 那么问题出现了,我是否关心这些类没有指定的初始化器? Probably not. 可能不是。 First, there are no other initializers in these classes, so I won't accidentally call the wrong one. 首先,这些类中没有其他初始化器,因此我不会意外地调用错误的初始化器。 Second, I'm not a programmer by training, so when I started writing apps, I used the procedural programming style that I was used to. 其次,我不是训练的程序员,所以当我开始编写应用程序时,我使用了我习惯的程序编程风格。 Until recently, I had never subclassed a class. 直到最近,我还没有将一个班级分类。 So I won't be subclassing this one and there won't be any subclasses to worry about. 所以我不会将这个子类化为子类,并且不会有任何子类需要担心。 Now that I know a bit more about Objective C, it turns out that every class I wrote was a subclass of one of iOS's classes and that actually explains a bit about why I was getting the errors. 现在我对Objective C有了更多的了解,结果发现我写的每个类都是iOS类之一的子类,这实际上解释了我为什么会得到错误。

To address Rob's point. 解决罗伯的观点。 I did not realize that I could create the table view object by calling a method on its superclass. 我没有意识到我可以通过调用其超类上的方法来创建表视图对象。 For example, this call works: 例如,此调用有效:

SettingsTableViewController *stvc = [[SettingsTableViewController alloc] initWithStyle: UITableViewStyleGrouped];

It works even when I have NS_DESIGNATED_INITIALIZER set. 即使我设置了NS_DESIGNATED_INITIALIZER,它也能正常工作。 Presumably no other warnings are sent because the compiler is already complaining about not calling a designated initializer of super. 可能没有发送其他警告,因为编译器已经在抱怨没有调用super的指定初始化程序。

And as long as the views called in the table view do not need any of the objects that were passed in, everything is fine. 只要在表视图中调用的视图不需要传入的任何对象,一切都很好。 If a view that is linked to in the table does need one of the objects, then obviously the app crashes. 如果在表中链接的视图确实需要其中一个对象,那么很明显应用程序崩溃了。

Since I never want to call anything but the designated initializer, following Rob's suggestion of using NSAssert(), I can make sure that I don't call the designated initializers of my superclass, and make all the warnings go away with this code: 因为除了指定的初始化程序之外我永远不想调用任何东西,遵循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;
}

I get this error in the log when I try to call initWithStyle directly. 当我尝试直接调用initWithStyle时,我在日志中收到此错误。

*** Assertion failure in -[SettingsTableViewController initWithStyle:], /Users/jscarry/Words/Words/Settings/SettingsTableViewController.m:37 ***断言失败 - [SettingsTableViewController initWithStyle:],/ Users / jscarry / Words / Words / Setting / SettingTableViewController.m:37

Useful links: iOS Designated Initializers : Using NS_DESIGNATED_INITIALIZER 有用的链接: iOS指定的初始化程序:使用NS_DESIGNATED_INITIALIZER

This article explains more about why it is implemented. 文章详细解释了为什么它的实现。

You can use this macro shared by @steipete . 您可以使用@steipete共享的此 Can solve the problem. 可以解决问题。

#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")

Usage: 用法:

// 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

Any not designated initializer get called will throw an assertion error. 任何未指定的初始化程序调用都会抛出一个断言错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 找不到超类&#39;-init&#39;的指定初始化方法的方法重写 - Method override for the designated initializer of the superclass '-init' not found Objective-C警告方法覆盖未找到超类&#39;-init&#39;的指定初始值设定项 - Objective-C warning Method override for the designated initializer of the superclass '-init' not found 是否可以使用便捷初始化程序覆盖指定的初始化程序? - Is it possible to override a designated initializer with a convenience initializer? 初始化程序不会覆盖指定的初始化程序Swift WatchKit - Initializer does not override a designated initializer Swift WatchKit 初始化程序不会覆盖其超类Swift 2.0中的指定初始化程序 - Initializer does not override a designated initializer from its superclass, Swift 2.0 初始化程序不会覆盖其父类中的指定初始化程序 - Initializer does not override a designated initializer from its superclass 从指定的初始化程序调用超静态方法 - Calling super static method from designated initializer UIViewController指定的初始值设定项vs loadView方法 - UIViewController designated initializer vs loadView method 将超类 init 方法链接到指定的初始化程序 - Link superclass init method to designated initializer 指定初始化器 - Designated Initializer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM