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

时间:2014-06-09 14:27:48

标签: 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'
    }
}

我不明白为什么不允许这样做。最终,每个类的指定初始值设定项都会调用它们需要的任何值,所以为什么我需要在B的{​​{1}}中重复一次,方法是再次为init指定一个默认值。 x中的便利init会不会很好?

7 个答案:

答案 0 :(得分:25)

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

  

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

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

强调我的。指定的初始化程序不能调用便利初始化程序。

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

Initializer Chaining

答案 1 :(得分: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()

因为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:)

答案 2 :(得分:12)

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

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()

会拨打SubClass.init(),呼叫SuperClass.init(value:),呼叫SubClass.init()

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

答案 3 :(得分:1)

我为此找到了解决方法。它不是非常漂亮,但它解决了不知道超类的值或想要设置默认值的问题。

您所要做的就是使用方便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) 
    }
}

答案 4 :(得分:1)

考虑从方便的init()中提取初始化代码到新的辅助函数foo(),调用foo(...)在子类中进行初始化。

答案 5 :(得分:0)

观看WWDC-video" 403中间Swift"在18:30深入解释初始化器及其继承。据我了解,请考虑以下事项:

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子类: 龙是龙,没有腿,没有翅膀。所以Wyvern的初始化器(2条腿,2个机翼)是错误的!如果不能简单地调用方便的Wyvern-Initializer,而只能调用完整的指定初始化程序,则可以避免该错误:

class Wyrm: Dragon {
    init() {
        super.init(legs: 0, isFlying: false)
    }
}

答案 6 :(得分:-1)

为什么你没有两个初始值设定项 - 一个具有默认值?

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