Epi*_*yte 155 ios sprite-kit swift
所以我今天更新到Xcode 6 beta 5,并注意到我几乎所有Apple类的子类都收到了错误.
错误说明:
类'x'不实现其超类的必需成员
这是我选择的一个例子,因为这个类目前非常轻量级,因此很容易发布.
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)
}
}
Run Code Online (Sandbox Code Playgroud)
所以我的问题是,为什么我收到此错误,我该如何解决?我没有实施什么?我正在打电话给指定的初始化程序.
Ben*_*ane 127
来自开发者论坛上的Apple员工:
"向编译器和构建的程序声明你真的不希望与NSCoding兼容的一种方法是做这样的事情:"
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
Run Code Online (Sandbox Code Playgroud)
如果您知道您不希望符合NSCoding,则可以选择此选项.我已经使用了很多我的SpriteKit代码,因为我知道我不会从故事板中加载它.
您可以采用的另一个选项是将方法实现为便利init,如下所示:
convenience required init(coder: NSCoder) {
self.init(stringParam: "", intParam: 5)
}
Run Code Online (Sandbox Code Playgroud)
请注意调用初始化程序self.这允许您只需要为参数使用虚拟值,而不是所有非可选属性,同时避免抛出致命错误.
第三种选择当然是在调用super时实现该方法,并初始化所有非可选属性.如果对象是从故事板加载的视图,则应采用此方法:
required init(coder aDecoder: NSCoder!) {
foo = "some string"
bar = 9001
super.init(coder: aDecoder)
}
Run Code Online (Sandbox Code Playgroud)
nhg*_*rif 71
现有答案中缺少两个绝对关键的特定于Swift的信息,我认为这有助于彻底清除这些信息.
required关键字标记该初始化程序.init方法的特殊继承规则.该文艺青年最爱的是这样的:
如果实现任何初始值设定项,则不再继承任何超类的指定初始值设定项.
您将继承的唯一初始值设定项(如果有)是超类便捷初始值设定项,它指向您碰巧覆盖的指定初始值设定项.
那么...准备好长版本?
init方法的特殊继承规则.我知道这是我提出的两点中的第二点,但我们无法理解第一点,或者为什么required关键字甚至存在,直到我们理解这一点.一旦我们理解了这一点,另一个变得非常明显.
All of the information I cover in this section of this answer is from Apple's documentation found here.
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.
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.
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:
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.
Rule 2 isn't particularly relevant to this conversation because SKSpriteNode's init(coder: NSCoder) is unlikely to be a convenience method.
So, your InfoBar class was inheriting the required initializer right up until the point that you added 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 initializers which didn't point to initializers we implemented).
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")
Run Code Online (Sandbox Code Playgroud)
Which presents the following error:
Missing argument for parameter 'bar' in call.
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 valid state for the object to be in. It's not a perfectly valid state for the Swift object to be in. self.bar is not an optional and cannot be 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:
class Bar: Foo {
var bar: String
}
Run Code Online (Sandbox Code Playgroud)
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:
Issue: Class 'Bar' has no initializers
Fix-It: Stored property 'bar' without initializers prevents synthesized initializers
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.
init(coder: NSCoder) at all? Why is it 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.
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.
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.
Consider this example:
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
Run Code Online (Sandbox Code Playgroud)
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
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).
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.
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.
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.
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.
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
Run Code Online (Sandbox Code Playgroud)
This generates the error:
Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer
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.
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.
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.
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:
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)
Run Code Online (Sandbox Code Playgroud)
This fails to compile.
The error message it gives is a little misleading:
Extra argument 'b' in call
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.
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.
mat*_*att 56
为什么会出现这个问题?嗯,显而易见的事实是它一直很重要(即在Objective-C中,因为我在Mac OS X 10.0中开始编程Cocoa的那天)来处理你的类不准备处理的初始化器.在这方面,文档一直非常清楚你的责任.但是,我们中有多少人为了履行它们而烦恼呢?可能我们都不是!并且编译器没有强制执行它们; 这一切都纯粹是传统的.
例如,在我的Objective-C视图控制器子类中使用此指定的初始化程序:
- (instancetype) initWithCollection: (MPMediaItemCollection*) coll;
Run Code Online (Sandbox Code Playgroud)
......我们传递一个真正的媒体项目集合是至关重要的:如果没有一个实例,实例就不可能存在.但我没有写任何"塞子"来防止有人用裸骨init来初始化我.我应该写一个(实际上,正确地说,我应该编写一个initWithNibName:bundle:继承的指定初始化器的实现); 但我太懒了,因为我"知道"我永远不会错误地初始化我自己的课程.这留下了一个巨大的漏洞.在Objective-C中,有人可以打电话给裸骨init,让我的ivars没有初始化,我们在没有桨的情况下上了小溪.
在大多数情况下,斯威夫特奇妙地将我从自己身上拯救出来.一旦我将这个应用程序翻译成Swift,整个问题就消失了.斯威夫特有效地为我制造了一个塞子!如果init(collection:MPMediaItemCollection)是我的类中声明的唯一指定的初始值设定项,我无法通过调用bare-bones来初始化init().这是一个奇迹!
在种子5中发生的事情仅仅是编译器已经意识到奇迹在这种情况下不起作用init(coder:),因为理论上这个类的实例可能来自一个nib,并且编译器无法阻止它 - 并且当nib加载,init(coder:)将被调用.所以编译器会让你明确地写下塞子.也很正确.
Gag*_*ngh 33
加
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
48543 次 |
| 最近记录: |