基礎知識
對象分為三種引用類方式 分別為 strong weak unowned 對象默認引用方式為 strong。strong 對象賦值時引用計數器加一 weak 和 unowned 對象賦值時引用計數器值不變,weak 類型定義時必須是optional 或者是 強制解包類型,unowned 對象必須要有默認值。
swift 閉包(closure)中定義在closure 外的對象引用計數器自動加一。
循環引用
weak引用
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
//圖一
john!.apartment = unit4A
unit4A!.tenant = john
//圖二

圖1

圖2
當設置 john 與 unit4A 為nil 時 john 和 unit4A 引用計數器都是1 對象不會被刪除。
john = nil
unit4A = nil

圖3
解決方案
在類定義時外部引用的成員變量設置成weak
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
//圖4

圖4
john = nil
//圖5

圖5
unit4A = nil
//圖6

圖6
unowned 引用
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
//圖7

圖7
john = nil
//圖8

圖8
john 對象設置為nil ,john引用計數器為0 john被析構,john.card引用計數器也為0 john 也被析構。
閉包引起的強引用循環
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
let heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
//closure 使用 closure 外部的對象時 外部對象的引用計數器+1
//圖9

圖9
解決方案
//定義closure時
lazy var asHTML: () -> String = {[weak self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
//或者
lazy var asHTML: () -> String = {[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
unowned 與weak 的區別
上面的例子 如果設置為weak 當外部的對象self 在closure內是一個optional 類型 設置為unowned closure 內部維持原狀
對象內部的closure 的生命周期比對象本身的周期時間長 建議不要使用unowned 使用weak