ios 类没有实现其超类的必需成员
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25126295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Class does not implement its superclass's required members
提问by Epic Byte
So I updated to Xcode 6 beta 5 today and noticed I received errors in nearly all of my subclasses of Apple's classes.
所以我今天更新到 Xcode 6 beta 5 并注意到我在 Apple 类的几乎所有子类中都收到了错误。
The error states:
错误指出:
Class 'x' does not implement its superclass's required members
类“x”未实现其超类所需的成员
Here is one example I picked because this class is currently pretty lightweight so it will be easy to post.
这是我选择的一个示例,因为这个类目前非常轻量级,因此很容易发布。
class InfoBar: SKSpriteNode { //Error message here
let team: Team
let healthBar: SKSpriteNode
init(team: Team, size: CGSize) {
self.team = team
if self.team == Team.TeamGood {
healthBar = SKSpriteNode(color: UIColor.greenColor(), size:size)
}
else {
healthBar = SKSpriteNode(color: UIColor.redColor(), size:size)
}
super.init(texture:nil, color: UIColor.darkGrayColor(), size: size)
self.addChild(healthBar)
}
}
So my question is, why am I receiving this error, and how can I fix it? What is it that I am not implementing? I'm calling a designated initializer.
所以我的问题是,为什么我会收到此错误,我该如何解决?我没有实施的是什么?我正在调用指定的初始化程序。
采纳答案by Ben Kane
From an Apple employee on the Developer Forums:
来自开发者论坛上的 Apple 员工:
"A way to declare to the compiler and the built program that you really don't want to be NSCoding-compatible is to do something like this:"
“向编译器和构建的程序声明你真的不想与 NSCoding 兼容的一种方法是做这样的事情:”
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
If you know you don't want to be NSCoding compliant, this is an option. I've taken this approach with a lot of my SpriteKit code, as I know I won't be loading it from a storyboard.
如果您知道您不想符合 NSCoding 标准,这是一个选项。我在很多 SpriteKit 代码中都采用了这种方法,因为我知道我不会从故事板加载它。
Another option you can take which works rather well is to implement the method as a convenience init, like so:
您可以采用的另一个效果很好的选项是将该方法实现为方便的 init,如下所示:
convenience required init(coder: NSCoder) {
self.init(stringParam: "", intParam: 5)
}
Note the call to an initializer in self
. This allows you to only have to use dummy values for the parameters, as opposed to all non-optional properties, while avoiding throwing a fatal error.
请注意对 中的初始化程序的调用self
。这允许您只需要为参数使用虚拟值,而不是所有非可选属性,同时避免抛出致命错误。
The third option of course is to implement the method while calling super, and initialize all of your non-optional properties. You should take this approach if the object is a view being loaded from a storyboard:
第三个选项当然是在调用 super 时实现该方法,并初始化所有非可选属性。如果对象是从故事板加载的视图,您应该采用这种方法:
required init(coder aDecoder: NSCoder!) {
foo = "some string"
bar = 9001
super.init(coder: aDecoder)
}
回答by nhgrif
There are two absolutely crucialpieces of Swift-specific information that are missing from the existing answers which I think help clear this up completely.
现有答案中缺少两个绝对重要的 Swift 特定信息,我认为它们有助于完全解决这个问题。
- If a protocol specifies an initializer as a required method, that initializer must be marked using Swift's
required
keyword. - Swift has a special set of inheritance rules regarding
init
methods.
- 如果协议指定了一个初始化器作为必需的方法,那么必须使用 Swift 的
required
关键字来标记该初始化器。 - Swift 有一套特殊的关于
init
方法的继承规则。
The tl;dris this:
该文艺青年最爱的是这样的:
If you implement any initializers, you are no longer inheriting any of the superclass's designated initializers.
如果你实现了任何初始化器,你就不再继承任何超类的指定初始化器。
The only initializers, if any, that you will inherit, are super class convenience initializers that point to a designated initializer which you happened to override.
您将继承的唯一初始值设定项(如果有)是超类便利初始值设定项,它们指向您碰巧覆盖的指定初始值设定项。
So... ready for the long version?
所以...准备好长版本了吗?
Swift has a special set of inheritance rules regarding init
methods.
Swift 有一套特殊的关于init
方法的继承规则。
I know this was the second of two points I made, but we can't understand the first point, or why the required
keyword even exists until we understand this point. Once we understand this point, the other one becomes pretty obvious.
我知道这是我提出的两点中的第二点,但required
在我们理解这一点之前,我们无法理解第一点,或者为什么关键字甚至存在。一旦我们理解了这一点,另一点就变得很明显了。
All of the information I cover in this section of this answer is from Apple's documentation found here.
我在本答案的这一部分中涵盖的所有信息均来自此处找到的 Apple 文档。
From the Apple docs:
来自苹果文档:
Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.Swift's approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.
与 Objective-C 中的子类不同,Swift 子类默认不继承其超类初始值设定项。Swift 的方法防止了来自超类的简单初始化器被更专业的子类继承并用于创建未完全或正确初始化的子类的新实例的情况。
Emphasis mine.
强调我的。
So, straight from the Apple docs right there, we see that Swift subclasses will not always (and usually don't) inherit their superclass's init
methods.
因此,直接从那里的 Apple 文档中,我们看到 Swift 子类并不总是(通常不会)继承其超类的init
方法。
So, when do they inherit from their superclass?
那么,他们什么时候从他们的超类继承呢?
There are two rules that define when a subclass inherits init
methods from its parent. From the Apple docs:
有两个规则定义子类何时init
从其父类继承方法。来自苹果文档:
Rule 1
If your subclass doesn't define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
规则1
如果您的子类没有定义任何指定的初始值设定项,它会自动继承其所有超类指定的初始值设定项。
规则 2
如果您的子类提供了其所有超类指定初始值设定项的实现(通过按照规则 1 继承它们,或通过提供自定义实现作为其定义的一部分),则它会自动继承所有超类便利初始值设定项。
Rule 2 isn't particularly relevant to this conversation because SKSpriteNode
's init(coder: NSCoder)
is unlikely to be a convenience method.
规则 2 与此对话并不是特别相关,因为SKSpriteNode
'sinit(coder: NSCoder)
不太可能是一种方便的方法。
So, your InfoBar
class was inheriting the required
initializer right up until the point that you added init(team: Team, size: CGSize)
.
因此,您的InfoBar
类一直继承required
初始化器,直到您添加init(team: Team, size: CGSize)
.
If you were to have not provided this init
method and instead made your InfoBar
's added properties optional or provided them with default values, then you'd have still been inheriting SKSpriteNode
's init(coder: NSCoder)
. However, when we added our own custom initializer, we stopped inheriting our superclass's designated initializers (and convenience initializerswhich didn't point to initializers we implemented).
如果你是没有提供这种init
方法,而是使你InfoBar
的添加的属性可选或默认值提供给他们,那么你就必须仍然被继承SKSpriteNode
的init(coder: NSCoder)
。然而,当我们添加自己的自定义初始化器时,我们停止继承超类的指定初始化器(以及不指向我们实现的初始化器的便利初始化器)。
So, as a simplistic example, I present this:
所以,作为一个简单的例子,我提出这个:
class Foo {
var foo: String
init(foo: String) {
self.foo = foo
}
}
class Bar: Foo {
var bar: String
init(foo: String, bar: String) {
self.bar = bar
super.init(foo: foo)
}
}
let x = Bar(foo: "Foo")
Which presents the following error:
其中出现以下错误:
Missing argument for parameter 'bar' in call.
调用中缺少参数“bar”的参数。
If this were Objective-C, it'd have no problem inheriting. If we initialized a Bar
with initWithFoo:
in Objective-C, the self.bar
property would simply be nil
. It's probably not great, but it's a perfectly validstate for the object to be in. It's nota perfectly valid state for the Swift object to be in. self.bar
is not an optional and cannot be nil
.
如果这是Objective-C,继承就没有问题。如果我们在 Objective-CBar
中用 with初始化initWithFoo:
,则self.bar
属性就是nil
。它可能不是很大,但它是一个非常有效的对象是在状态,它是不为斯威夫特对象是在一个完全有效的状态, self.bar
是不是可有可无,不能nil
。
Again, the only way we inherit initializers is by not providing our own. So if we try to inherit by deleting Bar
's init(foo: String, bar: String)
, as such:
同样,我们继承初始化器的唯一方法是不提供我们自己的。因此,如果我们尝试通过删除Bar
's来继承init(foo: String, bar: String)
,如下所示:
class Bar: Foo {
var bar: String
}
Now we're back to inheriting (sort of), but this won't compile... and the error message explains exactly why we don't inherit superclass init
methods:
现在我们回到继承(有点),但这不会编译......错误消息准确地解释了为什么我们不继承超类init
方法:
Issue:Class 'Bar' has no initializers
Fix-It:Stored property 'bar' without initializers prevents synthesized initializers
问题:“Bar”类没有初始值设定项
Fix-It:没有初始值设定项的存储属性“bar”会阻止合成初始值设定项
If we've added stored properties in our subclass, there's no possible Swift way to create a valid instance of our subclass with the superclass initializers which couldn't possibly know about our subclass's stored properties.
如果我们在子类中添加了存储属性,那么 Swift 就不可能使用超类初始化器创建子类的有效实例,而超类初始化器不可能知道我们子类的存储属性。
Okay, well, why do I have to implement init(coder: NSCoder)
at all? Why is it required
?
好吧,为什么我必须实施init(coder: NSCoder)
呢?为什么required
?
Swift's init
methods may play by a special set of inheritance rules, but protocol conformance is still inherited down the chain. If a parent class conforms to a protocol, its subclasses must to conform to that protocol.
Swift 的init
方法可能通过一组特殊的继承规则发挥作用,但协议一致性仍然沿链向下继承。如果父类符合协议,则其子类必须符合该协议。
Ordinarily, this isn't a problem, because most protocols only require methods which don't play by special inheritance rules in Swift, so if you're inheriting from a class that conforms to a protocol, you're also inheriting all of the methods or properties that allow the class to satisfy protocol conformance.
通常,这不是问题,因为大多数协议只需要在 Swift 中不受特殊继承规则影响的方法,所以如果你从一个符合协议的类继承,你也继承了所有的允许类满足协议一致性的方法或属性。
However, remember, Swift's init
methods play by a special set of rules and aren't always inherited. Because of this, a class that conforms to a protocol which requires special init
methods (such as NSCoding
) requires that the class mark those init
methods as required
.
但是,请记住,Swift 的init
方法遵循一组特殊的规则,并不总是可以继承的。因此,符合需要特殊init
方法(例如NSCoding
)的协议的类要求该类将这些init
方法标记为required
。
Consider this example:
考虑这个例子:
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
This doesn't compile. It generates the following warning:
这不编译。它生成以下警告:
Issue:Initializer requirement 'init(foo:)' can only be satisfied by a 'required' initializer in non-final class 'ConformingClass'
Fix-It:Insert required
问题:初始化器要求“init(foo:)”只能由非最终类“ConformingClass”中的“必需”初始化器满足
修复:需要插入
It wants me to make the init(foo: Int)
initializer required. I could also make it happy by making the class final
(meaning the class can't be inherited from).
它要我制作init(foo: Int)
所需的初始化程序。我也可以通过创建类来让它开心final
(这意味着类不能被继承)。
So, what happens if I subclass? From this point, if I subclass, I'm fine. If I add any initializers though, I am suddenly no longer inheriting init(foo:)
. This is problematic because now I'm no longer conforming to the InitProtocol
. I can't subclass from a class that conforms to a protocol and then suddenly decide I no longer want to conform to that protocol. I've inherited protocol conformance, but because of the way Swift works with init
method inheritance, I've not inherited part of what's required to conform to that protocol and I must implement it.
那么,如果我子类化会发生什么?从这一点来看,如果我进行子类化,我很好。如果我添加任何初始值设定项,我突然不再继承init(foo:)
. 这是有问题的,因为现在我不再符合InitProtocol
. 我不能从符合协议的类中继承子类,然后突然决定我不再想符合该协议。我继承了协议一致性,但由于 Swift 处理init
方法继承的方式,我没有继承符合该协议所需的部分内容,我必须实现它。
Okay, this all makes sense. But why can't I get a more helpful error message?
好吧,这一切都说得通。但为什么我不能得到更有用的错误信息?
Arguably, the error message might be more clear or better if it specified that your class was no longer conforming to the inherited NSCoding
protocol and that to fix it you need to implement init(coder: NSCoder)
. Sure.
可以说,如果错误消息指定您的类不再符合继承的NSCoding
协议并且要修复它,您需要实现init(coder: NSCoder)
. 当然。
But Xcode simply can't generate that message because that actually won't always be the actual problem with not implementing or inheriting a required method. There is at least one other reason to make init
methods required
besides protocol conformance, and that's factory methods.
但是 Xcode 根本无法生成该消息,因为这实际上并不总是不实现或继承所需方法的实际问题。除了协议一致性之外,至少还有一个其他原因来创建init
方法required
,那就是工厂方法。
If I want to write a proper factory method, I need to specify the return type to be Self
(Swift's equivalent of Objective-C's instanceType
). But in order to do this, I actually need to use a required
initializer method.
如果我想编写一个合适的工厂方法,我需要指定返回类型为Self
(Swift 相当于 Objective-C 的instanceType
)。但为了做到这一点,我实际上需要使用required
初始化方法。
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
This generates the error:
这会产生错误:
Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer
使用元类型值构造类类型“Self”的对象必须使用“必需”初始化程序
It's basically the same problem. If we subclass Box
, our subclasses will inherit the class method factory
. So we could call SubclassedBox.factory()
. However, without the required
keyword on the init(size:)
method, Box
's subclasses are not guaranteed to inherit the self.init(size:)
that factory
is calling.
这基本上是相同的问题。如果我们子类化Box
,我们的子类将继承类方法factory
。所以我们可以调用SubclassedBox.factory()
. 然而,如果没有required
对关键字init(size:)
的方法,Box
的子类都不能保证继承self.init(size:)
是factory
被调用。
So we must make that method required
if we want a factory method like this, and that means if our class implements a method like this, we'll have a required
initializer method and we'll run into the exact same problems you've run into here with the NSCoding
protocol.
所以required
如果我们想要一个这样的工厂方法,我们必须创建那个方法,这意味着如果我们的类实现了一个这样的方法,我们将有一个required
初始化方法,我们将遇到与你在这里遇到的完全相同的问题与NSCoding
协议。
Ultimately, it all boils down to the basic understanding that Swift's initializers play by a slightly different set of inheritance rules which means you're not guaranteed to inherit initializers from your superclass. This happens because superclass initializers can't know about your new stored properties and they couldn't instantiate your object into a valid state. But, for various reasons, a superclass might mark an initializer as required
. When it does, we can either employ one of the very specific scenarios by which we actually do inherit the required
method, or we must implement it ourselves.
最终,这一切都归结为基本的理解,即 Swift 的初始化器按照一组略有不同的继承规则发挥作用,这意味着您不能保证从超类继承初始化器。发生这种情况是因为超类初始值设定项无法知道您的新存储属性,并且它们无法将您的对象实例化为有效状态。但是,出于各种原因,超类可能会将初始化程序标记为required
. 当它发生时,我们可以使用我们实际继承该required
方法的非常具体的场景之一,或者我们必须自己实现它。
The main point here though is that if we're getting the error you see here, it means that your class isn't actually implementing the method at all.
不过,这里的要点是,如果我们收到您在此处看到的错误,则意味着您的类实际上根本没有实现该方法。
As perhaps one final example to drill in the fact that Swift subclasses don't always inherit their parent's init
methods (which I think is absolutely central to fully understanding this problem), consider this example:
作为最后一个示例,可以深入了解 Swift 子类并不总是继承其父类的init
方法(我认为这绝对是完全理解这个问题的核心),请考虑以下示例:
class Foo {
init(a: Int, b: Int, c: Int) {
// do nothing
}
}
class Bar: Foo {
init(string: String) {
super.init(a: 0, b: 1, c: 2)
// do more nothing
}
}
let f = Foo(a: 0, b: 1, c: 2)
let b = Bar(a: 0, b: 1, c: 2)
This fails to compile.
这无法编译。
The error message it gives is a little misleading:
它给出的错误信息有点误导:
Extra argument 'b' in call
调用中的额外参数“b”
But the point is, Bar
doesn't inherit any of Foo
's init
methods because it hasn't satisfied either of the two special cases for inheriting init
methods from its parent class.
但关键是,Bar
不继承任何Foo
的init
方法,因为它不满足init
从其父类继承方法的两种特殊情况中的任何一种。
If this were Objective-C, we'd inherit that init
with no problem, because Objective-C is perfectly happy not initializing objects' properties (though as a developer, you shouldn't have been happy with this). In Swift, this simply will not do. You can't have an invalid state, and inheriting superclass initializers can only lead to invalid object states.
如果这是 Objective-C,我们会init
毫无问题地继承它,因为 Objective-C 非常高兴不初始化对象的属性(尽管作为开发人员,您不应该对此感到满意)。在 Swift 中,这根本行不通。你不能有一个无效的状态,继承超类初始值设定项只能导致无效的对象状态。
回答by matt
Why has this issue arisen? Well, the plain fact is that it has alwaysbeen important (i.e. in Objective-C, since the day I started programming Cocoa back in Mac OS X 10.0) to deal with initializers that your class is not prepared to handle. The docs have always been quite clear about your responsibilities in this regard. But how many of us bothered to fulfill them, completely and to the letter? Probably none of us! And the compiler did not enforce them; it was all purely conventional.
为什么会出现这个问题?那么,显而易见的事实是,它总是很重要(即在Objective-C,因为那天我开始编程可可回在Mac OS X 10.0)来处理初始化你的类没有准备好处理。文档一直非常清楚您在这方面的责任。但是,我们中有多少人不厌其烦地完整地、一字不差地实现它们?可能我们都没有!编译器没有强制执行它们;这完全是传统的。
For example, in my Objective-C view controller subclass with this designated initializer:
例如,在我的带有这个指定初始值设定项的 Objective-C 视图控制器子类中:
- (instancetype) initWithCollection: (MPMediaItemCollection*) coll;
...it is crucial that we be passed an actual media item collection: the instance simply cannot come into existence without one. But I have written no "stopper" to prevent someone from initializing me with bare-bones init
instead. I shouldhave written one (actually, properly speaking, I should have written an implementation of initWithNibName:bundle:
, the inherited designated initializer); but I was too lazy to bother, because I "knew" I would never incorrectly initialize my own class that way. This left a gaping hole. In Objective-C, someone cancall bare-bones init
, leaving my ivars uninitialized, and we are up the creek without a paddle.
...向我们传递一个实际的媒体项集合是至关重要的:没有一个实例就无法存在。但是我没有写“阻止器”来防止有人用裸机来初始化我init
。我应该写一个(实际上,正确地说,我应该写一个initWithNibName:bundle:
继承的指定初始值设定项的实现);但我懒得打扰,因为我“知道”我永远不会以这种方式错误地初始化我自己的类。这留下了一个大洞。在Objective-C中,有人可以调用bare-bones init
,让我的ivars未初始化,我们就在没有桨的小溪上。
Swift, wonderfully, saves me from myself in most cases. As soon as I translated this app into Swift, the whole problem went away. Swift effectively creates a stopper for me! If init(collection:MPMediaItemCollection)
is the only designated initializer declared in my class, I can't be initialized by calling bare-bones init()
. It's a miracle!
在大多数情况下,Swift 奇妙地将我从自己的身边救了出来。一旦我将这个应用程序翻译成 Swift,整个问题就迎刃而解了。斯威夫特有效地为我创造了一个塞子!如果init(collection:MPMediaItemCollection)
是我的类中声明的唯一指定初始化程序,则无法通过调用裸机来初始化我init()
。这是一个奇迹!
What's happened in seed 5 is merely that the compiler has realized that the miracle doesn't work in the case of init(coder:)
, because in theory an instance of this class could come from a nib, and the compiler can't prevent that — and when the nib loads, init(coder:)
will be called. So the compiler makes you write the stopper explicitly. And quite right too.
种子 5 中发生的事情仅仅是编译器意识到奇迹在 的情况下不起作用init(coder:)
,因为理论上此类的实例可能来自笔尖,而编译器无法阻止这种情况 - 当笔尖加载,init(coder:)
将被调用。所以编译器让你显式地编写停止器。而且也很对。
回答by Gagan Singh
add
添加
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}