为什么Swift初始化器不能在超类上调用便捷初始化器?

Rob*_*ert 86 initialization class 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'
    }
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么不允许这样做.最终,每个类的指定初始化叫他们需要的任何值,那么为什么我要重复我自己Binit通过指定一个默认值x再次,当方便initA会做得很好?

Cra*_*tis 25

这是Swift编程指南中规定的"初始化链接"规则的规则1,其中包括:

规则1:指定的初始值设定项必须从其直接超类中调用指定的初始化程序.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

强调我的.指定的初始化程序不能调用便捷初始化程序.

有一个图表与规则一起展示了允许初始化程序"方向":

初始化链接

  • 但为什么这样做呢?文档只是说它简化了设计,但我不知道如果我不得不通过不断指定指定初始化器上的默认值来重复自己,我大多不关心在方便时的默认值初始化者会做什么? (75认同)
  • 希望他们允许称为便利初始化者.对于SDK中的某些类,除了调用便捷初始化器之外,没有其他方法可以实现某种行为.请参阅`SCNGeometry`:您只能使用便捷初始化程序添加`SCNGeometryElement',因此无法继承它. (8认同)
  • 我在这个问题后几天提交了一个错误17266917,要求能够打电话给方便初始者.还没有回复,但我也没有任何其他的Swift错误报告! (5认同)
  • 这是一个非常糟糕的语言设计决策.从我的新应用程序的一开始,我决定用swift开发我需要从我的子类调用NSWindowController.init(windowNibName),我就是不能这样做:( (4认同)
  • @Kaiserludi:没有什么用处,它以下面的回复结束:"这是设计的,任何相关的错误都已经在这个领域得到了解决." (4认同)

小智 21

考虑

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()
Run Code Online (Sandbox Code Playgroud)

因为A类的所有指定初始值设定项都被重写,所以B类将继承A的所有便利初始值设定项.因此执行此操作将输出

Entering A.init()
Entering A.init(a:)
Entering B.init(a:,b:)
Entering A.init(a:,b:)
Run Code Online (Sandbox Code Playgroud)

现在,如果指定的初始化程序B.init(a:b :)将被允许调用基类便捷初始化器A.init(a :),这将导致对B.init的递归调用(a:,b: ).

  • @kdazzle类的结构和类方法都不会改变.为什么他们呢? - 我能想到的唯一问题是,方便初始化器必须在继承时动态委托给子类的指定初始化器,并且在没有继承但是委托给子类时静态地委托给它自己的类的指定初始化器. (2认同)

Jul*_*ien 13

这是因为你最终可以获得无限递归.考虑:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

看看:

let a = SubClass()
Run Code Online (Sandbox Code Playgroud)

将调用SubClass.init()哪个将调用SuperClass.init(value:)哪个将调用SubClass.init().

指定/便利init规则被设计为类初始化始终是正确的.