在Swift子类中添加便捷初始化器
作为一个学习练习,我试图实现一个SKShapeNode
的子类,它提供了一个新的方便初始值设定项,它使用一个数字并构造一个数字宽度和高度的正方形的ShapeNode。
根据Swift书 :
规则1
如果你的子类没有定义任何指定的初始值设定项,它将自动inheritance它的所有超类指定的初始值设定项。
规则2
如果你的子类提供了它所有超类指定的初始化方法的实现,或者按照规则1inheritance它们,或者提供一个自定义实现作为其定义的一部分,那么它将自动inheritance所有的超类方便初始化方法。
但是,下面的类不起作用:
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
应inheritance所有SKShapeNode
的便捷初始值设定项,因为我没有实现任何我自己指定的初始值设定项,而且因为我的便捷初始值设定项正在调用另一个便捷初始值设定项init(rectOfSize)
,所以应该可以。 我究竟做错了什么?
我对Initializer Inheritance的理解和你的理解是一样的,我认为我们和本书的内容是一致的。 我不认为这是一个解释问题或对规定的误解。 也就是说,我认为你没有做错什么。
我在游乐场testing了以下内容,并按预期工作:
class RectShape: NSObject { var size = CGSize(width: 0, height: 0) convenience init(rectOfSize size: CGSize) { self.init() self.size = size } } class SquareShape: RectShape { convenience init(squareOfSize size: CGFloat) { self.init(rectOfSize: CGSize(width: size, height: size)) } }
RectShape
从NSObject
inheritance,并没有定义任何指定的初始值设定项。 因此,按照规则1,它inheritance了NSObject
所有指定的初始值。 我在实现中提供的便利构造器正确地委托给一个指定的构造器,然后进行实例的设置。
SquareShape
从SquareShape
inheritance,不提供指定的初始化程序,并且按照规则1再次inheritanceSquareShape
的所有指定初始值设定项。 按照规则2,它也inheritance了RectShape
定义的便利初始值设定RectShape
。 最后, SquareShape
定义的便利初始值设定SquareShape
正确地委托给inheritance的便利初始值设定程序,后者依次委托给inheritance的指定初始值设定项。
所以,鉴于你没有做错任何事情,我的例子按预期工作,我推断下面的假设:
由于SKShapeNode
是用Objective-C编写的,规定“每个便利初始值设定程序必须从同一个类调用另一个初始值设定项”的规则不是由该语言强制执行的。 所以,也许SKShapeNode
的方便初始化器实际上不会调用指定的初始化器。 因此,尽pipe子类MyShapeNode
按预期MyShapeNode
inheritance了方便初始值设定项,但它们没有正确地委托给inheritance的指定初始值设定项。
但是,这又是一个假设。 我所能确认的是,这些机制在我自己创build的两个class级上按预期工作。
这里有两个问题:
-
SKShapeNode只有一个指定的初始化程序:
init()
。 这意味着我们不能在不调用init()
情况下退出我们的初始化程序。 -
SKShapeNode有一个声明为
CGPath!
的属性path
CGPath!
。 这意味着我们不想在没有初始化path
情况下跳出我们的初始化程序。
这两件事的结合是问题的根源。 简而言之,SKShapeNode的写法不正确。 它有一个必须初始化的属性path
; 因此它应该有一个指定的初始化程序来设置path
。 但它不(所有的path
初始化器都是便捷初始化器)。 这是错误。 换句话说,问题的根源是,方便与否, shapeNodeWith...
方法根本不是初始化器。
然而,你可以做你想做的事情 – 写一个便利的初始化器,而不用强迫写任何其他的初始化器 – 通过满足这个顺序的两个要求 ,即像这样写:
class MyShapeNode : SKShapeNode { convenience init(squareOfSize value: CGFloat) { self.init() self.init(rectOfSize: CGSizeMake(value, value)) } }
这看起来是非法的,但事实并非如此。 一旦我们调用了self.init()
,我们已经满足了第一个要求,现在我们可以自由地引用self
(在调用self.init()
,我们不再使用self来委托initializer)错误)并满足第二个要求。
在Matt的回答的基础上,我们不得不包含一个额外的函数,否则编译器抱怨调用一个没有参数的初始化器。
以下是SKShapeNode的子类:
class CircleNode : SKShapeNode { override init() { super.init() } convenience init(width: CGFloat, point: CGPoint) { self.init() self.init(circleOfRadius: width/2) // Do stuff } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }