首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >所需的初始化器与指定的初始化器之间有什么区别?

所需的初始化器与指定的初始化器之间有什么区别?
EN

Stack Overflow用户
提问于 2017-01-11 17:12:32
回答 1查看 5.1K关注 0票数 16

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

‘必需’初始化器‘init(编码器:)’必须由'UITableViewCell‘子类提供。

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

苹果文档说:

必需的初始化器:

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

指定的初始化器

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

下列陈述是否正确:

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

尽管如此,我仍然不完全理解他们的功能差异。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-01-11 17:27:39

虽然关联的关键字requiredconvenience都用于指定子类的限制,但所需的初始化符和指定的初始化符实际上并不相关。

所需初始化器

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

代码语言:javascript
复制
protocol TestProtocol {
    init()
}

class TestClass: TestProtocol {
    required init() {

    }
}

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

有了所需的初始化器,您就可以在编译时初始化类,而不知道它是什么,这对各种原因都是有用的:

代码语言:javascript
复制
let classType: TestProtocol.Type = TestClass.self
let object = classType.init()

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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)。指定的初始化器必须确保类的所有属性在初始化器完成之前都有一个值(或者调用一个超级初始化项)。方便初始化程序没有这个要求,因为它们自己必须调用指定的初始化器。

代码语言:javascript
复制
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关键字,因为这些关键字都用于指示子类的初始化规则,而子类只有classes支持:required关键字表示子类必须提供该初始化项,而convenience关键字表示子类不能调用初始化器。尽管没有关键字,但它们仍然必须提供在它们所遵循的任何协议中定义的初始化符,并且您可以编写调用self.init的“方便的”初始值,而不必使用convenience关键字。

对你的发言作出答复:

  • 不需要指定所需的首字母。
  • 指定的首字母不必是必需的。
  • 类可以有多个必需的和指定的初始化符。
票数 21
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41596785

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档