Swift 类构造器的使用
这几天在使用 Swift 重写原来的一个运动社交应用 SportJoin. 为什么要重写呢? 首先因为实在找不到设计师给我作图; 其次,原来写的代码太烂了我也闲不下来,想找一些项目做,所以只好将原来的代码重写了. 原来的代码大约是一年半以前写的,我现在真的不想吐槽当时写的代码有多烂,有一句话怎么说来着:
个人觉得这句话还是蛮有道理的. 反正对于我来说,每过一段的时间回过头来看自己写的代码都感觉有很大的重构空间,很多地方写的不够 PERFECT,虽然我不是一个处女座,但是对于代码的健壮和整洁还是很注意的. 接下来,我来扯一扯谈一谈最近写 Swift 遇到的那些坑问题吧. 感受首先说下 Swift 给我带来的感受吧,Swift 的刚开始使用的时候感觉还是太特么难用了可以的. 不过 Xcode 在 Swift 上的补全极其慢,因为 Swift 所有的属性方法都是默认公开的,所以可能是因为每次都要搜索全局的符号导致自动补全非常缓慢,严重影响了工作效率,有同样的问题的请戳这里. 当然也不排除我电脑配置的影响,不过重写的过程还是蛮顺利的,没有遇到太多的问题,而且使用了很多 Swift 的高级特性来缩减原来冗长的 ObjC 代码. 标题好了然后,谈一下我在这两天中写 Swift 时遇到的最大问题 —- 构造器 init 的使用. 注: 我们在这篇博客中提到的构造器都为类构造器,在这里不提及值构造器的使用,详见文档. 刚刚使用这个构造器的时候我感觉到很困惑啊,不就是个 init,你给我搞这么多事情干什么? 我只想安安静静地初始化 开始使用 init当我遵从以前写 ObjC 的习惯,在 Swift 中键入 init 之后,编译器提醒我:
这是什么意思(,#?Д?),好吧,这个错误竟然可以点. 于是开心地双击,然后呢,Xcode 在我们的屏幕中自动生成了这些东西: required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
随后,我就如在 ObjC 中一样在 这是啥意思?
必须调用一个
既然说 convenience 构造器不能调用 super.init,那么按照错误提示改成 self.init 应该就好了.
既然报了这个错误,那么如果加上
找不到 init 方法接收所提供参数的重载. 最后一个常见的错误大概是这样的 Orz,到这里我已经放弃了自己通过尝试来解决这些问题了. 于是我求助于 Google,最后怒看苹果的官网文档并找到了以上错误的全部答案. 使用 init 方法的正确姿势苹果的官方文档关于构造器的部分请戳这里 在 Swift 中,类的初始化有两种方式,分别是 Designated Initializer 指定构造器在一个类中必须至少有一个,而便利构造器的数量没有限制. 指定构造器(Designated Initializer)
指定构造器是类的主要构造器,要在指定构造器中初始化所有的属性,并且要在调用父类合适的指定构造器. 每个类应该只有少量的指定构造器,大多数类只有一个指定构造器,我们使用 Swift 做 iOS 开发时就会用到很多 UIKit 框架类的指定构造器,比如说: init() init(frame: CGRect) init(style: UITableViewCellStyle,reuseIdentifier: String?)
这些都是类指定构造器,并且这些方法的前面是没有任何的关键字的(包括 当定义一个指定构造器的时候,必须调用父类的某一个指定构造器: init(imageName: String,prompt: String = "") {
super.init(style: .Default,reuseIdentifier: nil)
...
}
在这里我们的指定构造器调用了父类的指定构造器 便利构造器(Convenience Initializer)
便利构造器是类的次要构造器,你需要让便利构造器调用同一个类中的指定构造器,并将这个指定构造器中的参数填上你想要的默认参数. 如果你的类不需要便利构造器的话,那么你就不必定义便利构造器,便利构造器前面必须加上 在这里我们就不举例了,但是我们要提一下便利构造器的语法: convenience init(parameters) { statements }
init 规则定义 init 方法必须遵循三条规则 指定构造器必须调用它直接父类的指定构造器方法. 在图中,只有指定构造器才可以调用父类的指定构造器,而便利构造器是不可以的,这也遵循了我们之前所说的三条规则. 只要 init 方法遵循这三个规则就不会有任何问题.
init 机制在 Swift 中一个实例的初始化是分为两个阶段的
而这与 ObjC 的区别主要在于第一部分,因为在 ObjC 中所有的属性如果不赋值都会默认被初始化为 Swift 的编译器会对初始化的方法进行安全地检查已保证实例的初始化可以被安全正确的执行:
接下来我们来说明一下类构造的两个阶段: 阶段 1
阶段 2 从顶部构造器链一直向下,每个构造器链中类的指定构造器都有机会进一步定制实例. 构造器此时可以访问 self,修改它的属性并调用实例方法等等。
init 的继承和重载
跟 ObjC 不同,Swift 中的子类默认不会继承来自父类的所有构造器. 这样可以防止错误的继承并使用父类的构造器生成错误的实例(可能导致子类中的属性没有被赋值而正确初始化). 与方法不同的一点是,在重载构造器的时候,你不需要添加 override 关键字. 虽然子类不会默认继承来自父类的构造器,但是我们也可以通过别的方法来自动继承来自父类的构造器,构造器的继承就遵循以下的规则:
错误分析我们到目前为止已经基本介绍了所有的构造器使用的注意事项,接下来我们分析一下最开始错误的原因. 错误 1 第一个错误是因为,我们一开始虽然没有为指定构造器提供实现,不过,因为重载了指定构造器,所以来自父类的指定构造器并不会被继承.
而 错误 2 class TableViewCell: UITableViewCell {
init() {
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
我们已经手动覆写了这个方法,然后,因为
所以我们让这个指定构造器调用 class TableViewCell: UITableViewCell {
init() {
super.init(style: .Default,reuseIdentifier: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
错误 3 class TableViewCell: UITableViewCell {
convenience init() {
super.init(style: .Default,reuseIdentifier: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
所以我们让这个便利构造器调用同一个类的 class TableViewCell: UITableViewCell {
convenience init() {
self.init(style: .Default,reuseIdentifier: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
而这段代码目前还是有问题的,而这就是 错误 4 错误 4 的主要原因就是重载了父类的
class TableViewCell: UITableViewCell {
convenience init() {
self.init(style: .Default,reuseIdentifier: nil)
}
}
只需要删掉这个 错误 5 class TableViewCell: UITableViewCell {
let label : UILabel
init(imageName: String) {
super.init(style: .Default,reuseIdentifier: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
因为 init(imageName: String) {
self.label = UILabel()
super.init(style: .Default,reuseIdentifier: nil)
}
这是第一个解决的办法,不过我一般使用另一种,在属性定义的时候就为他说初始化一个值. class TableViewCell: UITableViewCell {
let label = UILabel()
init(imageName: String) {
super.init(style: .Default,reuseIdentifier: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这些就是我在使用 swift 的构造其中遇到的全部错误了. 总结 调用相关
指定构造器必须调用它直接父类的指定构造器方法.
便利构造器必须调用同一个类中定义的其它初始化方法.
便利构造器在最后必须调用一个指定构造器.
属性相关
指定构造器必须要确保所有被类中提到的属性在代理向上调用父类的指定构造器前被初始化,之后才能将其它构造任务代理给父类中的构造器.
指定构造器必须先向上代理调用父类中的构造器,然后才能为任意属性赋值.
便利构造器必须先代理调用同一个类中的其他构造器,然后再为属性赋值.
构造器在第一阶段构造完成之前,不能读取任何实例属性的值,self 不能被引用.
继承相关
如果子类没有定义任何的指定构造器,那么会默认继承所有来自父类的指定构造器.
如果子类提供了所有父类指定构造器的实现,不管是通过上一条规则继承过来的,它将自动继承所有父类的便利构造器.
Swift 中的构造器 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |