必需的初始化器和指定的初始化器有什么区别?

What's the difference between a required initializer and a designated initializer?

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

'required' initializer 'init(coder:)' must be provided by subclass of 'UITableViewCell'

我查了一下,显然这也是实现它的必要条件。但这导致我对必需的和指定的初始化器感到困惑

Apple Docs 说:

所需的初始化程序:

Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:

指定初始化器

Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.

下列说法正确的是:

话虽如此,我仍然不完全了解它们的功能差异。

必需的初始化器和指定的初始化器并没有真正相关,尽管相关的关键字 requiredconvenience 都用于指定对 subclasses 的限制。

必需的初始化程序

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

protocol TestProtocol {
    init()
}

class TestClass: TestProtocol {
    required init() {

    }
}

这里,required关键字必须存在,因为TestClass的任何子classes也必须提供init()(因为它们也符合TestProtocol).

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

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

如果您的 class 符合多个协议,例如每个协议都有不同的初始化程序,则还必须要求每个初始化程序:

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 关键字。

即使您没有任何协议,您仍然可以通过初始化编译时未知的 class 类型来使用 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 的所有属性在初始化程序完成(或调用超级初始化程序)之前都有一个值。便利初始化器只是没有这个要求,因为它们必须自己调用指定的初始化器。

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 这一事实,因此如果您希望 class 是 sub[=80,请确保您没有任何方便的初始化程序提供您指定的初始化程序不提供的功能=]编辑!


结构和枚举不使用 requiredconvenience 关键字,因为这些关键字都用于指示子 class 的初始化规则,只有 classes支持:required关键字表示subclasses必须提供那个初始化器,convenience关键字表示subclasses不能调用那个初始化器。尽管没有关键字,它们仍然必须提供在它们遵循的任何协议中定义的初始化程序,并且您可以编写 'convenient' 调用 self.init 的初始化程序,只是没有 convenience 关键字。


回应您的发言:

  • 不需要指定必需的初始化器。
  • 不需要指定的初始化器。
  • 类 可以有多个必需的和指定的初始化器。