Apple 在 iOS 5 之後開放 ARC,開發者不需要再自行 release or retain 來操作記憶體,
swift 的 ARC 與 Objc 並沒有差異太大,開發者需要注意的是 code 之間的記憶體管理,
避免造成 memory leak 等等問題。
ARC 只對 reference type 有用,與 structure 或是 enumeration 等 value type 並無相關。
ARC 範例
class MyClass {
deinit {
print("deinit")
}
}
var c: MyClass? = MyClass()
var c1 = c
var c2 = c
c = nil
c1 = nil
c2 = nil
Strong reference cycle
當指定 instance 給予變數或常數時,稱為 strong reference,
每當有一個 strong reference 指向 instance 時,此 instance 的 reference 就會加一,
當 instance 的 reference 歸零時,此 instance 的記憶體就會被系統回收。
在以下的情況,會讓 instance 的 reference 沒有歸零卻也沒有變數或常數 reference,
導致無法被記憶體回收而造成 memory leak。
class MyClass {
var c: MyClass2?
deinit {
print("myClass deinit")
}
}
class MyClass2 {
var c: MyClass?
deinit {
print("myClass2 deinit")
}
}
var c: MyClass? = MyClass()
var c2: MyClass2? = MyClass2()
c!.c = c2
c2!.c = c
c = nil
c2 = nil
在上述的兩個 class 內各自擁有對方的變數,初始化出兩個 instance 後,互相指定給對方的 property,
這時候執行 c = nil 時,因為 c2 的 property 還有 strong reference 到 c,因此 c 不會被回收,
執行 c2 = nil 時,因為沒有被回收到 c 的 property 還有 strong reference 到 c2,
因此 c2 也不會被回收,最後的情況變成這兩個 instance 都不會被回收卻也無法再被使用到。
c (strong)-> MyClass instance (strong)-> c2
c2 (strong)-> MyClass2 instance (strong)-> c
MyClass instance (strong)-> c2
c2 (strong)-> MyClass2 instance (strong)-> c
MyClass instance (strong)-> c2
MyClass2 instance (strong)-> c
要解決 strong reference cycle 的方法,可以使用 weak reference 或是 unowned reference,
若使用這兩個 reference 指向 instance 時,此 instance 的 reference 並不會加一。
class MyClass {
var c: MyClass2?
deinit {
print("myClass deinit")
}
}
class MyClass2 {
weak var c: MyClass?
deinit {
print("MyClass2 deinit")
}
}
var c: MyClass? = MyClass()
var c2: MyClass2? = MyClass2()
c!.c = c2
c2!.c = c
c = nil
c2 = nil
重新來看,初始化出兩個 instance 後,互相指定給對方的 property,
這時候執行 c = nil 時,因為 c2 的 property 是 weak reference 到 c,
因此 c 此時的 reference 已經歸零並且被回收,
執行 c2 = nil 時,因為 c 已經被回收,因此 c2 的 reference 也會歸零並且被回收。
c (strong)-> MyClass instance (strong)-> c2
c2 (strong)-> MyClass2 instance (weak)-> c
c2 (strong)-> MyClass2
Unowned reference 跟 weak reference 用法相同,但是 unowned reference 必須要有值,
因此不可以使用 optional,所以會用在非常確定不會被回收到的 instance 上。
class MyClass {
var c: MyClass2?
deinit {
print("myClass deinit")
}
}
class MyClass2 {
unowned let c: MyClass
init(para:MyClass) {
c = para
}
deinit {
print("myClass2 deinit")
}
}
var c: MyClass? = MyClass()
c!.c = MyClass2(para:c!)
c = nil
在 closures 內也會發生 strong reference cycle,如果 class 內有 closure,
並且在 body 內去呼叫了 self,當 instance 執行時,便會對 instance 產生 strong reference。
class MyClass {
let s = "123"
lazy var property: () -> String = {
return self.s
}
}
var c: MyClass? = MyClass()
let s = c.property
c = nil
Swift 提供在 closures 內加入 unowned reference 與 weak reference。
class MyClass {
let s = "123"
lazy var property: () -> String = { [unowned self = self]
return self.s
}
}
var c: MyClass? = MyClass()
let s = c.property
c = nil