所需的初始化程序和指定的初始化程序有什么区别? [英] What's the difference between a required initializer and a designated initializer?

查看:81
本文介绍了所需的初始化程序和指定的初始化程序有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我查找了它,显然也必须实现它.但这导致我对必需的初始化器和指定的初始化器感到困惑

Apple Docs 说:

必需的初始化程序:

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

指定的初始化程序

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

以下陈述是否正确:

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

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

解决方案

尽管关联的关键字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
    }
}

(这是一个非常人为的例子.)

根据我的经验,便利初始化程序很少有用,我倾向于发现可以使用指定的初始化程序上的可选参数来解决它们所解决的问题.还需要考虑以下事实:初始化器不能调用其父类上的便捷初始化程序,因此请确保您没有任何便捷初始化程序,这些便捷初始化程序提供了如果您打算将您的类归为子类时指定的初始化程序不提供的功能!


结构和枚举不使用requiredconvenience关键字,因为这些关键字均用于指示子类的初始化规则,只有class支持:required关键字指示子类必须提供该初始化程序,而convenience关键字指示子类无法调用该初始化程序.尽管没有关键字,但它们仍必须提供在它们遵循的任何协议中定义的初始化程序,并且您可以编写调用self.init的便捷"初始化程序,而无需使用convenience关键字.


要回应您的陈述:

  • 不必指定必需的初始化程序.
  • 不必指定指定的初始化程序.
  • 类可以具有多个必需的和指定的初始化程序.

I was creating my own custom tableViewCell and then I got an error saying:

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

I looked it up and obviously it's a must to implement that as well. But this led to my confusion about required vs. designated initializers

Apple Docs says:

Required Initializers:

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

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.

Are the following statements correct:

  • A required initializer is always a designated initializer
  • Every designated initializer is not necessarily a required initializer
  • A class can only have one required initializer, however it can have multiple designated initializers?

Having that said I still don't fully understand their functional differences.

解决方案

Required initialisers and designated initialisers are not really related, though the associated keywords required and convenience are both used to specify restrictions on subclasses.

Required Initialisers

A required initialiser makes a guarantee that you can initialise a type, or any of its sub-types, with that initialiser. If you have an initialiser in a protocol and you conform something to that protocol, you have to use required (if it's a class) because that protocol guarantees that the initialiser is present on that class, and any of its subclasses. When you use required on an initialiser of a class, that signals that all of its subclasses can also be initialised using that method. This means you also need to add that initialiser to any of its subclasses.

protocol TestProtocol {
    init()
}

class TestClass: TestProtocol {
    required init() {

    }
}

Here, the required keyword must be present because any subclasses of TestClass must also provide init() (because they also conform to TestProtocol).

Having a required initialiser allows you to initialise a class without knowing what it is at compile time, which is useful for a variety of reasons:

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

If your class conformed to multiple protocols, each with a different initialiser for example, each of those initialisers must also be required:

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
    }
}

Note that adding super.init() isn't required in this special case, because Swift will automatically include the call if it takes no parameters.

In all the above examples, the initialisers are designated because they do not include the convenience keyword.

Even if you didn't have any protocols, you can still make use of required by initialising a type of a class which isn't known at compile time:

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)

Designated Initialisers

A designated initialiser is one which isn't a convenience initialiser (i.e, marked with convenience). A designated initialiser must make sure that all properties of the class have a value before the initialiser finishes (or a super initialiser is called). Convenience initialisers only don't have this requirement because they must themselves call a designated initialiser.

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
    }
}

(This is fairly contrived example.)

In my experience, convenience initialisers are rarely useful and I tend to find the problems they solve can be solved using optional arguments on designated initialisers instead. One also needs to consider the fact that initialisers can't call convenience initialisers on their superclass, so make sure you don't have any convenience initialisers which provide functionality that your designated initialisers don't if you intend your class to be subclassed!


Structs and enums don't use the required or convenience keywords because these keywords are both used to indicate initialisation rules for subclasses, which only classes support: The required keyword indicates that subclasses must provide that initialiser, and the convenience keyword indicates that subclasses cannot call that initialiser. Despite not having the keywords, they must still provide initialisers defined in any protocols they conform to, and you can write 'convenient' initialisers which call self.init, just without the convenience keyword.


To respond to your statements:

  • Required initialisers don't have to be designated.
  • Designated initialisers don't have to be required.
  • Classes can have multiple required and designated initialisers.

这篇关于所需的初始化程序和指定的初始化程序有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆