Swift中的指定构造器和便利构造器 Designated/Convenience Initializers

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看159 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 159|回复: 1
收起左侧

Swift中的指定构造器和便利构造器 Designated/Convenience Initializers

[复制链接]
cocoaswift 发表于 2016-6-27 12:46:40 | 显示全部楼层 |阅读模式
快来登录
获取最新的苹果动态资讯
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码

今天我们将了解有关类构造器的另一个部分,Swift 语言提供了两个不同类型的构造器,分别称为指定构造器便利构造器,他们其实已经存在于 Objective-C 中,但到了 Swift 中它们有了点小变化,并且也引入了一个非常有用的关键字。下面我们将讨论一下这个话题,看看它们如何结合起来使你的类更加好用。

Swift中的指定构造器和便利构造器 Designated/Convenience Initializers 1

Swift中的指定构造器和便利构造器 Designated/Convenience Initializers - 敏捷大拇指 - Swift中的指定构造器和便利构造器 Designated/Convenience Initializers 1





指定构造器 Designated Initializers

像 Swift 中大部分的构造器,指定构造器的名字就非常恰当地解释了它所做的工作。它们是一个类最主要的构造器。一个类必须有一个指定构造器,但并不是说只能有一个。如果必要,你可以声明多个,但是通常情况一个就足够了。

下面我们来简单看看指定构造器是什么样子:

[Swift] 纯文本查看 复制代码
init(sender: String, recipient: String) {
    self.sender = sender
    self.recipient = recipient

    timeStamp = NSDate()
}


是不是看起来十分熟悉呢?我们之前有篇文章 Classes In Swift — An Introduction and Using a Nested Type in Swift (英文) 里介绍了一个 Message 类,这就是创建它的一个指定构造器的语法。我们的这个类只有一个构造器,所以它必须是指定构造器。创建它你只需要用到 init 这个关键字,配合几个需要用到的参数,就大功告成了。




2、便利构造器 Convenience Initializers

便利构造器的作用也正如其名,他们就是用来让构造类的过程更简单。如果说指定构造器需要把所有的属性初始化好,并让用户自己传递这些属性的值,那便利构造器就是把其中几个属性硬编码了,这样我们就能用尽量少的参数去构造一个相同的对象了。通常,我们用便利构造器将对象用一些默认值进行初始化来让它们适合某种使用场景。

上面的构造器也许应该是一个便利构造器,因为它设置了 timeStamp 属性而没有让用户指定任何值。像我上面所说的,我认为指定构造器需要让用户传递所有需要设置的属性值(除非有些属性值是由其他参数值衍生计算而来的)。但这也只是个人偏好啦,上面那种写法作为指定构造器也是完全合法的,毕竟我们说 timeStamp 就是 Message 对象被构造的时间嘛。

尽管如此,让我们为我们的类添加一个便利构造器,让这个对象表示“它发送一个消息给它自己”。在这种情况下,我们就不需要传递两个不同的参数了,因为发送者就是接受者,所以我们声明一个只接受一个参数的便利构造器:

[Swift] 纯文本查看 复制代码
convenience init(sender: String) {
    self.init(sender: sender, recipient: sender)
}


它们在语法上是不同的,便利构造器有个 convenience 关键字在 init 之前,其他方面到时非常相似。因为我们只取了一个参数,所以我们就把这一个参数分别传递给指定构造器的两个参数。

Swift中的指定构造器和便利构造器 Designated/Convenience Initializers 2

Swift中的指定构造器和便利构造器 Designated/Convenience Initializers - 敏捷大拇指 - Swift中的指定构造器和便利构造器 Designated/Convenience Initializers 2





3、两种构造器的使用原则

Swift 有三个有关两种构造器相互使用的原则,这里我直接引用 Apple 的 iBook 原文,就不进行解释了:

  • 一个指定构造器必须调用它直系父类的一个指定构造器。
  • 一个便利构造器必须调用这个类自身的另一个构造器。
  • 一个便利构造器最终一定会调用一个指定构造器。

引用自:Apple Inc. “The Swift Programming Language” iBooks


所以你能看到我们的便利构造器满足了上述的第 2 和 3 条原则。我们的指定构造器也处在类层次的最顶端,因此我们不需要调用父类的构造器(以满足第一天原则)。如果你重新调用了它,一定是它有一个子类,就像下面这个 TextMessage 类:

[Swift] 纯文本查看 复制代码
init(content: String, sender: String, recipient: String) {
    self.content = content
    super.init(sender: sender, recipient: recipient)
}


你看,它满足了第一条原则了。我们首先设置了这个类自身的 content 属性,然后调用了父类的指定构造器,这就完成这个类的初始化了。这也是和 Objective-C 的差异,我们在 Objective-C 中是先初始化父类,再设置自己的属性。当然,回到 Swift 中,如果需要你也可在调用完其父类后再设置继承来的属性。

根据 WWDC 的一个视频,这显然取决于类继承的工作方式。举个例子,如果一个父类调用了一个子类复写的方法,父类实际上将会调用子类复写后的方法实现(因为已经被复写了嘛),如果我们没有完全初始化好我们子类的属性,并且复写方法依赖于他们,那我们就遇到麻烦了。

这些原则有一些细微差别。在第二条原则中,一个便利构造器必须调用另一个构造器,其实不必是指定构造器,随便一个构造器都可以。如果你需要,你可以声明几个便利构造器,然后相互调用,这种链式调用时没问题的。但最终还是要符合第三条原则就对了。

举个例子吧:

[Swift] 纯文本查看 复制代码
convenience init() {
    self.init(content: "")
}

convenience init(content: String) {
    self.init(content: content, sender: "Myself")
}

convenience init(content: String, sender: String) {
    self.init(content: content, sender: sender, recipient: sender)
}


你能看到无参便利构造器传递了一个默认参数调用了另外一个便利构造器,另一个又是如此,但最终指定构造器还是被调用了。

我们之前声明的便利构造器设置了相同的 sender 和 recipient 来达到某些目的,为什么不子类化一个新类来解决这个问题呢?那么...

[Swift] 纯文本查看 复制代码
class NoteMessage: Message {
    let content: String

    init(content: String, theUser: String) {
        self.content = content
        super.init(sender: theUser)
    }


额,看来我们想当然了,别忘了第一条原则,一个指定构造器必须直接调用其直系父类的指定构造器。但我们尝试去调用它的一个便利构造器了,这看来是不行的。

我不知道为什么 Swift 设计成这样,毕竟一个便利构造器最终都会调用一个指定构造器。也许这迫使我们去思考最合适的默认属性值,而不是只顾便利。又或许以后这点会改变?谁知道呢。




4、总结

All code in this post was tested against a playground in Xcode 7.3.1.

Designated initializers perform the actual initialization for a class’s properties.  Convenience initializers let you program defaults into simpler initializers with less input parameters, and they hand off the actual initialization back to the designated initializers.  You don’t have to have any convenience initializers if you wish, but they are quite useful in certain situations.

I am particularly happy about the addition of the convenience keyword in Swift.  In Objective-C, as far as i know, the only way to know which was the designated initializer was to look in the comments in the header file (assuming you do not have access to the code).  If they did not comment about it, and you don’t have the code, I think you would just have to guess.  If you had the code, the designated initializer is the one that calls the superclass’s initializer (similar to rule 1 above).

Clearly marking a convenience initializer as such just makes it easier to know.  In our class, it was rather obvious, but it may not be so obvious for all classes.

(译者按)

老外写文章有点啰嗦了,我这里也就不总结了,如果你认真阅读了本文,我相信你对指定构造器和便利构造器一定有了很清晰的认识。总的来说就是,便利构造器是为你类的初始化工作提供方便的,它们最终一定要依赖于那些真正使你类能正常工作的初始化工作,这也就是指定构造器的工作。





译者:Cyandev

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +10 贡献 +10 专家分 +10 收起 理由
Anewczs + 10 + 10 + 10 32个赞!专家给力!

查看全部评分

baddy 发表于 2016-6-27 17:12:26 | 显示全部楼层
头一次听说构造器还有这分类的
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表