15

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

“必需”初始化程序“init(coder:)”必须由“UITableViewCell”的子类提供

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

苹果文档说:

必需的初始化程序:

在类初始值设定项的定义之前写入 required 修饰符以指示该类的每个子类都必须实现该初始值设定项:

指定初始化器

指定初始化器是类的主要初始化器。指定的初始化程序完全初始化该类引入的所有属性,并调用适当的超类初始化程序以继续沿超类链的初始化过程。

以下陈述是否正确:

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

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

4

1 回答 1

20

必需的初始化器和指定的初始化器并不真正相关,尽管相关的关键字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
    }
}

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

根据我的经验,便利初始化器很少有用,我倾向于发现它们解决的问题可以使用指定初始化器上的可选参数来解决。还需要考虑一个事实,即初始化程序不能在其超类上调用便利初始化程序,因此请确保您没有任何便利初始化程序提供您指定的初始化程序所不具备的功能,如果您希望您的类被子类化!


结构体和枚举不使用requiredorconvenience关键字,因为这些关键字都用来表示子类的初始化规则,只有classes 支持:required关键字表示子类必须提供那个初始化器,convenience关键字表示子类不能调用那个初始化器。尽管没有关键字,但它们仍然必须提供在它们符合的任何协议中定义的初始化程序,并且您可以编写调用 的“方便”初始化程序,self.init而无需convenience关键字。


回应您的陈述:

  • 不必指定所需的初始化程序。
  • 不一定需要指定的初始化程序。
  • 类可以有多个必需的和指定的初始化器。
于 2017-01-11T17:27:39.607 回答