作为一个学习练习,我试图实现一个 SKShapeNode
的子类,它提供了一个新的便利初始化器,它接受一个数字并构造一个ShapeNode,它是一个数字宽度和高度的正方形 .
根据Swift Book:
规则1如果您的子类没有定义任何指定的初始值设定项,它会自动继承其所有超类指定的初始值设定项 . 规则2如果您的子类提供了所有超类指定初始化器的实现 - 通过按照规则1继承它们,或者通过提供自定义实现作为其定义的一部分 - 那么它会自动继承所有超类便捷初始化器 .
但是,以下类不起作用:
class MyShapeNode : SKShapeNode {
convenience init(squareOfSize value: CGFloat) {
self.init(rectOfSize: CGSizeMake(value, value))
}
}
相反,我得到:
Playground execution failed: error: <REPL>:34:9: error: use of 'self' in delegating initializer before self.init is called
self.init(rectOfSize: CGSizeMake(value, value))
^
<REPL>:34:14: error: use of 'self' in delegating initializer before self.init is called
self.init(rectOfSize: CGSizeMake(value, value))
^
<REPL>:35:5: error: self.init isn't called on all paths in delegating initializer
}
我的理解是 MyShapeNode
应该继承所有 SKShapeNode
的便利初始化器,因为我没有实现任何我自己的指定初始化器,并且因为我的便利初始化器调用 init(rectOfSize)
,另一个便利初始化器,这应该工作 . 我究竟做错了什么?
3 回答
我对Initializer继承的理解与你的相同,我认为我们都与本书所述的内容完全一致 . 我认为这不是解释问题或对所述规则的误解 . 那就是说,我认为你做错了什么 .
我在Playground测试了以下内容,它按预期工作:
RectShape
继承自NSObject
并且未定义任何指定的初始值设定项 . 因此,根据规则1,它继承了NSObject
的所有指定初始值设定项 . 在执行intance的设置之前,我在实现中提供的便利初始化程序正确地委托给指定的初始化程序 .SquareShape
继承自RectShape
,不提供指定的初始值设定项,并且再次按照规则1继承SquareShape
的所有指定初始值设定项 . 根据规则2,它还继承了RectShape
中定义的便利初始化程序 . 最后,SquareShape
中定义的便利初始化程序正确地委托给继承的便利初始化程序,后者又委托给继承的指定初始化程序 .所以,鉴于你没有做错任何事情以及我的例子按预期工作,我推断以下假设:
由于
SKShapeNode
是用Objective-C编写的,因此该规则规定"every convenience initializer must call another initializer from the same class"不是由该语言强制执行的 . 因此,也许SKShapeNode
的便利初始化程序实际上并不调用指定的初始化程序 . 因此,即使子类MyShapeNode
按预期继承了便利初始值设定项,它们也无法正确委托给继承的指定初始值设定项 .但是,这只是一个假设 . 我所能确认的是,这些机制在我自己创建的两个类中按预期工作 .
这里有两个问题:
SKShapeNode只有一个指定的初始值设定项:
init()
. 这意味着如果不调用init()
,我们就无法退出初始化程序 .SKShapeNode的属性
path
声明为CGPath!
. 这意味着我们不想在不以某种方式初始化path
的情况下退出初始化程序 .这两件事的结合是问题的根源 . 简而言之,SKShapeNode编写错误 . 它有一个必须初始化的属性
path
;因此它应该有一个指定的初始化程序来设置path
. 但它没有(它的所有path
设置初始化器都是便利初始化器) . 这就是错误 . 换句话说,问题的根源是,方便与否,shapeNodeWith...
方法根本不是真正的初始化器 .但是,你可以做你想做的事情 - 编写一个方便的初始化程序而不必强制写任何其他初始化程序 - 通过按顺序满足这两个要求,即通过这样写:
它看起来是非法的,但它不是't. Once we',我们满足了第一个要求,我们现在可以自由地引用
self
(我们不再得到"use of 'self' in delegating initializer before self.init is called"错误)并满足第二个要求 .基于Matt的答案,我们必须包含一个额外的函数,否则编译器抱怨调用一个没有参数的初始化器 .
以下是SKShapeNode的子类: