所需的初始化程序和指定的初始化程序之间有什么区别?

时间:2017-01-11 17:12:33

标签: swift initialization designated-initializer

我正在创建自己的自定义tableViewCell,然后我收到一条错误说:

  

'required'初始化程序'init(编码器:)'必须由子类提供   '的UITableViewCell'

我查了一下,显然也是必须实现的。但这导致我对必需与指定初始化器的混淆

Apple Docs说:

必需的初始化程序:

  

在定义类之前编写必需的修饰符   初始化程序,表示该类的每个子类都必须   实现初始化程序:

指定初始值设定项

  

指定的初始值设定项是类的主要初始值设定项。一个   指定的初始化程序完全初始化所引入的所有属性   该类并调用适当的超类初始化程序继续   初始化过程超级链。

以下陈述是否正确:

  • 必需的初始化程序始终是指定的初始化程序
  • 每个指定的初始化程序不一定是必需的 初始化
  • 一个类只能有一个必需的初始值设定项,但它可以有多个指定的初始值设定项?

说完后我仍然不完全理解他们的功能差异。

1 个答案:

答案 0 :(得分:16)

必需的初始化工具和指定的初始化工具并不真正相关,但关联的关键字requiredconvenience都用于指定对子类的限制。

必需的初始化程序

必需的初始化程序保证您可以使用该初始化程序初始化类型或其任何子类型。如果您在协议中有一个初始化器并且您符合该协议,则必须使用required(如果它是一个类),因为该协议保证初始化器存在于该类及其任何子类中。在类的初始化器上使用required时,表示也可以使用该方法初始化其所有子类。这意味着您还需要将该初始化程序添加到其任何子类中。

protocol TestProtocol {
    init()
}

class TestClass: TestProtocol {
    required init() {

    }
}

此处,required关键字必须存在,因为TestClass的任何子类也必须提供init()(因为它们也符合TestProtocol)。

拥有一个必需的初始化程序允许你在不知道编译时它是什么的情况下初始化一个类,这有很多原因:

let classType: TestProtocol.Type = TestClass.self
let object = classType.init()

如果您的班级符合多个协议,例如每个协议都有不同的初始化程序,那么每个初始化程序也必须是必需的:

protocol OtherProtocol {
    init(thing: Int)
}

class OtherClass: TestClass, OtherProtocol {
    let thing: Int

    required init() { // Required from superclass/its protocol
        self.thing = 0
    }

    required init(thing: Int) { // Required from new protocol
        self.thing = thing
    }
}

请注意,在这种特殊情况下不需要添加super.init(),因为如果不带参数,Swift会自动包含调用。

在上述所有示例中,指定了初始值者,因为它们不包含convenience关键字。

即使你没有任何协议,你仍然可以通过初始化编译时未知的类的类型来使用required

class BaseClass {
    let value: Int

    required init(value: Int) {
        self.value = value
    }
}

class SubClass: BaseClass {
    required init(value: Int) { // Required from superclass
        super.init(value: value) // Must call desginated initialiser of superclass
    }
}

let someBaseClassType: BaseClass.Type = SubClass.self
let someBaseClassInstance = someBaseClassType.init(value: 1)

指定的初始化程序

指定的初始化程序是一个不是便利初始化程序(即标有convenience)的程序。指定的初始化程序必须确保在初始化程序完成之前(或调用超级初始化程序),类的所有属性都具有值。便利初始化者不具备此要求,因为他们必须自己调用指定的初始化者。

class OtherSubClass: BaseClass {
    convenience required init(value: Int) {
        self.init() // Must call designated initialiser of this class
    }

    init() {
        super.init(value: 0) // Must call designated initialiser of superclass
    }
}

(这是一个相当人为的例子。)

根据我的经验,便利初始化器很少有用,我倾向于发现他们解决的问题可以使用指定初始化器上的可选参数来解决。还需要考虑initialisers can't call convenience initialisers on their superclass这一事实,因此请确保您没有任何便利初始化程序,如果您打算将您的类划分为子类,那么这些初始化程序将提供您指定的初始化程序所不具备的功能!

结构和枚举不使用requiredconvenience关键字,因为这些关键字都用于表示子类的初始化规则,只有class支持:{{1 }} keyword表示子类必须提供该初始化,required关键字表示子类不能调用该初始化。尽管没有关键字,但他们仍然必须提供在其符合的任何协议中定义的初始化工具,并且您可以编写“方便”的初始化工具,只需要convenience关键字即可调用self.init

回复你的陈述:

  • 无需指定所需的初始化程序。
  • 不一定要求指定的初始化者。
  • 类可以有多个必需和指定的初始化程序。
相关问题