403 Intermediate Swift
此篇是WWCD 2014 Session 403 的学习笔记。主要涉及 Optionals、Memory Management、Initialization、Closure、Matching Pattern 这些主题。
Optionals
这个概念,肯定会在学习 Swift 过程中掉坑里。
使用 Optional type
是用来表示缺失值,主要为确保类型安全。声明 optional 变量时,在声明类型后加一个 ?
可以把任意的 optional 想象成一个盒子,在未实际打开这个盒子之前,它总是存在两种情况:要么有东西,要么是空盒。
1 | var optionalNumber: Int? // default initialized to nil |
Non-Optional Type
顾名思义,那 Swift 中规定一个 Non-Optional can’t be nil ,也就是
1 | var optionalString: String = nil // 会引发 compile error |
Unwrapped Optionals
! 是 ImplicitlyUnwrappedOptional\
Session 中有给出一个具体的例子,再回顾一次。
1 | func findIndexOfString(string: String, array: String[]) -> Int? { |
Optional Binding
其定义是: test and unwrap at the same time,即 optional 配合 if let 一起使用,这样可以从逻辑上和可读性上更为简洁。上述的例子中,改写一下: if indexValue = index // 这个时候已经 unwrap 可以直接使用 index 的内部值了。
当需要多处链环 optional banding 的业务逻辑(if多层嵌套)时,引出 Optional Chaining 的概念。同一条链上东西,只要其中一环是 nil,那么整条链就会返回 nil ;对 nil 做 forced unwrap 则抛出 runtime errorOptional Chaining
其定义:combine with optional binding to unwrap
1 | if let addressNumber = paul.residence?.address?.buildingNumber?.toInt() |
在任意对象解除引用之后(renters = nil 和或 apts = nil ),被引用的 Person 和 Apartment 之间由于已形成 Strong Ref cycle,导致 Person 和 Apartment 的 2 个 instances 牢牢地相互绑定,谁也无法从中抽身。
为了打破僵局,引入 Weak Reference
的做法,定义:A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. 在声明成员变量的时,前置 weak
关键字。 且注意 所有的 weak ref 都是 optional value,因此无法声明成 let
一旦 deallocate 任意变量,renters = nil 后,即再没有 Ref 指向 Person ,所以 Person Instance 得以 deinit。 之后,如果 apts = nil,那么Apartment Instance 也被 deinit。
另外在方法调用过程中,也存在 Strong / Weak Ref
if let tenant = apt.tenant {
tenant.buzzIn() // BINDING the optional produces a Strong Ref
}
等价于
apt.tenant?.buzzIn()
if apt.tenant {
apt.tenant!.buzzIn() // TESTING a Weak Ref alone does not produce a Strong Ref
}
在调用链上不会保存 Strong Ref
Same-Lifetime Relationships
上述的例子发生在两个(生命周期独立的)类中,对实例变量的 ownership 产生的 Strong Ref 的化解。接下来,看一下对有着相同生命周期的对象之间的引用关系。下面这个例子使用到 Unowned Reference
定义:an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is assumed to always have a value. 可见,unowned 和 Weak 相似,本质的区别在于 unowned ref 总是存在一个确定的值。
CreditCard.holder 必须存在一个确定的值,因此在此声明为 unowned 。 如果不这样做,当 customers = nil ,由于 Strong Ref Cycle 的作用,再次导致任意 instance 无法 deiniticalization。 声明成 unowned,customers = nil 之后, Person 的 instance 可以 deinit, 之后没有对 CreditCard 的 Strong Ref, 那么 CreditCard 的 instance 也可以 deinit。
Strong、Weak、Unowned Reference 总结
- 从 owner 方使用 Strong Ref 表明它们对 objects 的绝对拥有权。(被 Ref 的 Objects ,只要存在一根 Ref 关系线就不能 deinit)
- 在生命周期内相互独立的 Objects 之间,使用 Weak Ref。(Ref 线以虚线表示,实际上对 Objects 的 deinit 不造成影响)
- 当 owner 和 objects 有相同的生命周期,那么使用 unowned Ref。(与 weak 的区别还在于 object 必须有确定值)
Initialization
Session 中强调数遍的简单规则:每个值在使用前 必须 被初始化。 如果某个值没有被初始化过(可能是逻辑覆盖不完整,同样会导致编译错误)
Initializers
负责一个 instance 的全部初始化工作, init()
Structure Initialization
在 init() 内不能缺少对任何一个成员变量的初始化,必须全部覆盖到,这个做法同样是满足 Rule 的要求。如果存在类似 mutating func validateColor() {…} 的方法,不允许放在变量初始化前,因为会调用 self.validateColor() 而这时候的 self 尚未初始化完毕。如果 Struct 没有提供显式的 init(),在声明实例时 依照变量名的声明顺序 进行初始化赋值。
Class Initialization
这里有一个比较重要的概念是,Swift 总是初始化全部的成员变量之后,再调用 super.init() 这点跟 ObjC 的做法相反。
Convenience Initialiers
前置 Convenience 关键字,可以为 class 声明不同的 init。在 init 内部需要手动调用 self.init(…) 来匹配对应的 init 版本。
Initializer Inheritance
Initialization 总结
Rule: 在使用(某个实例)之前 必须 初始化所有的值。
首先设置所有的 Stored properties, 之后再调用 super.init 方法初始化父类
在实例消灭前存在析构方法 deinit ,你可以使用它干些 clean work,如果有必要的话..多数情况不用
Designated initializers only delegate UP (子类能对父类进行 纵向 的 init 操作: super.init)
Convenience initializers only delegate ACROSS (在同一个类中进行 横向 的 init 操作: self.init。convenince init 不能调用 super.init)
Closures
Session 首先给出了一个字符串 sort 的具体例子,然后按特性一步步简化代码,并利用 Trailing Closures 演示 Functional Programming 的可能性。
var clients = ["Pestov", "Buenaventura", "Sceeram", "Babbage"]
cliens.sort({(a:String, b:String)->Bool in
return a < b // 可进行各类 Bool 判定,比如字符串长度
})
Type Inference
struct Array\Implicit Returen
clients.sort({a,b in a < b})Implicit Arguments
clients.sort({ $0 < $1 })Trailing Closures
clients.sort { $0 < $1 }Functional Programming
println("..." + words.filter{$0.hasSuffix("gry")}
.map{$0.uppercaseString}
.reduce("HULK"){"\($0) \($1)"} + "!!!")
Closures 同样会出现 Strong Ref 问题
原因是 Closure 全是 ARC Objects