ARC是如何工作的
每次創(chuàng)建一個新的類的實例,ARC便會分配一塊內存來存儲實例的信息
當一個實例不再需要,ARC便會釋放掉它的內存
為了做到這一點焕济,當將一個類的實例分配給一個屬性嘴瓤、常量或變量,該屬性汰蜘、常量或變量都會強引用該實例。該實例不被釋放直到?jīng)]有強引用之宿。
循環(huán)引用
循環(huán)引用發(fā)生在實例之間的相互引用
循環(huán)引用的例子
首先創(chuàng)建了兩個類
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?
創(chuàng)建實例
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
給變量設置
john!.apartment = unit4A
unit4A!.tenant = john
以上兩個實例之間創(chuàng)建了循環(huán)引用族操,當打破john和unit4A的強引用時,引用計數(shù)不會降為0比被,并且實例不會被ARC釋放掉
john = nil
unit4A = nil
如何解決循環(huán)引用
在swift中有兩種解決循環(huán)引用的方式:weak引用和unowned引用
weak 和 unowned 不會對實例保持強引用
使用weak的情景:當被引用的實例的生命周期相對短色难,因此該實例被首先釋放
使用unowned的情景:當被引用的實例有同樣生命周期或相對長
Weak 引用
因為weak 引用不會強引用實例,所以仍然被weak引用的實例有可能被釋放掉等缀。當weak引用的實例被釋放時枷莉,ARC自動設置此weak引用為nil。因為weak引用需要允許它的值變?yōu)閚il尺迂,所以它們總是聲明為可選變量
注意:當ARC將弱引用設置為nil時笤妙,不會調用屬性觀察器。
這個例子與上面的區(qū)別僅僅是設置tenant變量為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
john = nil
unit4A = nil
Unowned 引用
和weak引用一樣蹲盘,不會強引用實例。不一樣的是膳音,unowned引用用于被引用 的實例的有相同的生命的周期或相對長辜限。
一個unowned引用被期望總是有值的,因此严蓖,ARC決不會設置unowned引用為nil薄嫡,意味著unowned不定義為可選類型。
重要:僅僅當被引用的變量總是不會釋放時使用unowned引用
以下這個例子與上面公寓與人之間的關系略有不同颗胡。在這個數(shù)據(jù)模型中毫深,一個客戶可能有也可能沒有信用卡,但是信用卡將始終與一個客戶相關聯(lián)毒姨。
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!)
設置Customer實例為nil哑蔫,Customer便沒有了強引用被釋放掉,然后CreditCard實例也沒有了強引用被釋放
john = nil
閉包的強引用
閉包會捕捉變量并強引用變量
閉包是引用類型,當分配一個閉包給實例的屬性闸迷,此實例就強引用了閉包嵌纲,如果閉包訪問了此實例的屬性,例如self.someProperty
腥沽,閉包會捕捉self并強引用逮走,此時循環(huán)引用就發(fā)生了
循環(huán)引用產(chǎn)生的例子
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")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil
實例引用計數(shù)不為0不會釋放
如何解決閉包的循環(huán)引用
定義捕獲列表:
捕獲列表中的實例用weak或unowned關鍵字來修飾,寫在一對方括號內今阳,用逗號隔開师溅。
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
使用weak引用:捕捉的變量有可能為nil,意味著被捕捉的變量的生命周期短于閉包
使用unowned引用:總是相互引用盾舌,并同時釋放墓臭,說明被捕捉的變量的生命周期等于大于閉包
通過定義捕捉變量來解決循環(huán)引用
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
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")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
當設置HTMLElement實例的為nil時,此實例便被釋放
paragraph = nil