Class 为什么可以';Swift初始化器在其超类上调用便利初始化器吗?
以两类为例:Class 为什么可以';Swift初始化器在其超类上调用便利初始化器吗?,class,initialization,swift,Class,Initialization,Swift,以两类为例: class A { var x: Int init(x: Int) { self.x = x } convenience init() { self.init(x: 0) } } class B: A { init() { super.init() // Error: Must call a designated initializer of the superclass 'A'
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
我不明白为什么这是不允许的。最终,每个类的指定初始值设定项都会被调用它们所需的任何值,那么为什么我需要在a
中方便的init
会很好的时候,通过再次指定x
的默认值,在B
中重复自己的init
呢?这是“初始值设定项链接”的规则1Swift编程指南中规定的规则,内容如下:
规则1:指定的初始值设定者必须从他们的
直接超类
我的。指定的初始值设定项不能调用便利初始值设定项
有一个符合规则的图表来演示允许哪些初始值设定项“方向”:
请观看18:30的WWDC视频“403 intermediate Swift”,深入了解初始值设定项及其继承。正如我理解的,考虑以下内容:
class Dragon {
var legs: Int
var isFlying: Bool
init(legs: Int, isFlying: Bool) {
self.legs = legs
self.isFlying = isFlying
}
convenience initWyvern() {
self.init(legs: 2, isFlying: true)
}
}
但现在考虑一个WYRM子类: Wyrm是一条没有腿也没有翅膀的龙。所以Wyvern(两条腿,两个翅膀)的初始值设定项是错误的!如果不能调用方便的Wyvern初始值设定项,而只能调用完整的指定初始值设定项,则可以避免该错误:
class Wyrm: Dragon {
init() {
super.init(legs: 0, isFlying: false)
}
}
考虑
class A
{
var a: Int
var b: Int
init (a: Int, b: Int) {
print("Entering A.init(a,b)")
self.a = a; self.b = b
}
convenience init(a: Int) {
print("Entering A.init(a)")
self.init(a: a, b: 0)
}
convenience init() {
print("Entering A.init()")
self.init(a:0)
}
}
class B : A
{
var c: Int
override init(a: Int, b: Int)
{
print("Entering B.init(a,b)")
self.c = 0; super.init(a: a, b: b)
}
}
var b = B()
因为类A的所有指定初始值设定项都被重写,所以类B将继承A的所有方便初始值设定项。因此执行此操作将输出
Entering A.init()
Entering A.init(a:)
Entering B.init(a:,b:)
Entering A.init(a:,b:)
现在,如果允许指定的初始值设定项B.init(a:B:)调用基类便利初始值设定项a.init(a:),这将导致递归调用B.init(a:,B:)。我找到了解决方法。它不是超级漂亮,但它解决了不知道超类的值或想要设置默认值的问题
您所要做的就是使用子类的
init
中方便的init
创建一个超类的实例。然后使用刚才创建的实例调用super的指定init
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
// calls A's convenience init, gets instance of A with default x value
let intermediate = A()
super.init(x: intermediate.x)
}
}
为什么不使用两个初始值设定项—一个具有默认值
class A {
var x: Int
init(x: Int) {
self.x = x
}
init() {
self.x = 0
}
}
class B: A {
override init() {
super.init()
// Do something else
}
}
let s = B()
s.x // 0
这是因为你可以得到一个无限的递归。考虑:
class SuperClass {
init() {
}
convenience init(value: Int) {
// calls init() of the current class
// so init() for SubClass if the instance
// is a SubClass
self.init()
}
}
class SubClass : SuperClass {
override init() {
super.init(value: 10)
}
}
看看:
let a = SubClass()
它将调用子类.init()
它将调用超类.init(值:)
它将调用子类.init()
指定的/便利初始化规则设计为类初始化总是正确的。考虑将初始化代码从便利的
init()
提取到新的帮助函数foo()
,调用foo(…)
在子类中进行初始化。但是为什么这样做呢?文档只是说它简化了设计,但如果我必须不断重复自己的做法,在指定的初始值设定项上指定默认值,我不太关心便利初始值设定项中的默认值何时可以使用?在这个问题后的几天,我提交了一个bug 17266917,要求能够打电话给方便的初始化者。还没有回应,但我也没有收到任何关于我的其他Swift错误报告!希望他们能允许调用方便的初始化器。对于SDK中的某些类,除了调用便利初始化器之外,没有其他方法可以实现某种行为。请参见SCNGeometry
:您只能使用方便的初始化器添加SCNGeometryElement
s,因此无法从中继承。这是一个非常糟糕的语言设计决策。从我的新应用程序一开始,我就决定用swift开发,我需要从我的子类中调用NSWindowController.init(windowNibName),但我无法做到这一点:(@Kaiserludi:没什么用处,它以以下响应关闭:“这是设计的,任何相关的bug都已经在这一领域解决了。”这不是一个真正的原因。如果我在调用initWyvern
有意义时创建一个子类会怎么样?是的,我不相信。在调用方便的初始值设定项后,Wyrm
没有什么可以阻止它重写腿数。这是WWDC视频中给出的原因(仅适用于cars->racecars和布尔hasTurbo属性)。如果子类实现了所有指定的初始值设定项,那么它也会继承方便的初始值设定项。我有点明白其中的意义,而且我也确实对Objective-C的工作方式没有太大问题。请参见在init中调用super.init last的新约定,而不是像在Objective-C中那样首先调用super.init“last”在概念上不是新的,它与objective-c中的相同,只是在那里,所有的东西都被赋予了一个初始值(nil,0,随便什么)自动。只是在Swift中,我们必须自己进行这个阶段的操作系统初始化。这种方法的一个好处是,我们还可以选择指定不同的初始值。我搜索了一个答案,但我找不到任何能满足我的答案。这可能是一些实现原因。可能在另一个系统中搜索指定的初始值设定项类比搜索方便的初始值设定项…或类似的东西容易得多。@Robert,谢谢你在下面的评论。我想你可以将它们添加到你的问题中,甚至可以发布一个你收到的答案:“这是设计的,任何相关的bug都已经在这一领域解决了。”。因此,看起来他们不能或不想解释原因。这很容易解决。只要您从指定的初始值设定项中调用超类的便利初始值设定项,超类的便利初始值设定项将不再被继承。@fluidsonic但这将是疯狂的,因为您的结构和类方法将发生变化g取决于它们的使用方式。想象一下调试的乐趣吧!@kdazzle类的结构和类的方法都不会改变。为什么要改变呢?--