在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)) } } 

RectShapeNSObjectinheritance,并没有定义任何指定的初始值设定项。 因此,按照规则1,它inheritance了NSObject所有指定的初始值。 我在实现中提供的便利构造器正确地委托给一个指定的构造器,然后进行实例的设置。

SquareShapeSquareShapeinheritance,不提供指定的初始化程序,并且按照规则1再次inheritanceSquareShape的所有指定初始值设定项。 按照规则2,它也inheritance了RectShape定义的便利初始值设定RectShape 。 最后, SquareShape定义的便利初始值设定SquareShape正确地委托给inheritance的便利初始值设定程序,后者依次委托给inheritance的指定初始值设定项。

所以,鉴于你没有做错任何事情,我的例子按预期工作,我推断下面的假设:

由于SKShapeNode是用Objective-C编写的,规定“每个便利初始值设定程序必须从同一个类调用另一个初始值设定项”的规则不是由该语言强制执行的。 所以,也许SKShapeNode的方便初始化器实际上不会调用指定的初始化器。 因此,尽pipe子类MyShapeNode按预期MyShapeNodeinheritance了方便初始值设定项,但它们没有正确地委托给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") } }