自動引用計數(shù)的工作機制
// 下面的例子展示了自動引用計數(shù)的工作機制
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
// 定義了三個 Peroson? 類型的變量姨涡,用來按照代碼中的順序回溺,為新的 Person 實例設置多個引用杖玲。由于可選類型的變量會被自動初始化為一個 nil 值抡谐,目前還不會引用到 Person 類的實例
var reference1: Person?
var reference2: Person?
var reference3: Person?
// Person 實例并且將它賦值給三個變量中的一個:
reference1 = Person(name: "John Appleseed")
// prints "John Appleseed is being initialized"
// 將同一個 Person 實例分配給了兩個變量铁追,則該實例又會多出兩個強引用:
reference2 = reference1
reference3 = reference1
// 你通過給其中兩個變量賦值 nil 的方式斷開兩個強引用(包括最先的那個強引用)姻成,只留下一個強引用, Person 實例不會被銷毀:
reference1 = nil
reference2 = nil
// 在你清楚地表明不再使用這個 Person 實例時昙楚,直到第三個也就是最后一個強引用被斷開時 ARC 會銷毀它近速。
reference3 = nil
// prints "John Appleseed is being deinitialized"
循環(huán)強引用
// 解決循環(huán)強引用問題,可以通過定義類之間的關(guān)系為弱引用( weak )或無主引用( unowned )來代替強引用
// 房子可以沒人桂肌,人也可以沒房子(即都可以為nil)
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
// 即使都nil 也不會走deinit方法~
john = nil
unit4A = nil
解決實例之間的循環(huán)強引用
// Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環(huán)強引用問題:弱引用( weak reference )和無主引用( unowned reference )数焊。
弱引用 (在前面加上關(guān)鍵字 weak)
// 弱引用:弱引用不會對其引用的實例保持強引用,因而不會阻止 ARC 釋放被引用的實例
// 所以說實例被釋放了弱引用仍舊引用著這個實例也是有可能的
// 由于弱引用需要允許它們的值為 nil 崎场,它們一定得是可選類型。
// 人可以nil,但是信用卡必須綁定一個人(即 一個可nil遂蛀,一個不可nil)
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
// prints "John Appleseed is being reinitialized"
// 由于再也沒有強引用到 Person 它被釋放掉了并且 tenant 屬性被設置為 nil :
unit4A = nil
// prints "Apartment 4A is being deinitialized"
無主引用 (在前面加上關(guān)鍵字 unowned )
// 和弱引用類似谭跨,無主引用不會牢牢保持住引用的實例。
// 但是不像弱引用李滴,總之螃宙,無主引用假定是永遠有值的。
// 因此所坯,無主引用總是被定義為非可選類型谆扎。
// 由于無主引用是非可選類型,你不需要在使用它的時候?qū)⑺归_芹助。無主引用總是可以直接訪問堂湖。不過 ARC 無法在實例被釋放后將無主引用設為 nil ,因為非可選類型的變量不允許被賦值為 nil 状土。
// 如果你試圖在實例的被釋放后訪問無主引用无蜂,那么你將觸發(fā)運行時錯誤。只有在你確保引用會一直引用實例的時候才使用無主引用蒙谓。
// 還要注意的是斥季,如果你試圖訪問引用的實例已經(jīng)被釋放了的無主引用,Swift 會確保程序直接崩潰累驮。你不會因此而遭遇無法預期的行為酣倾。所以你應當避免這樣的事情發(fā)生。(空指針異常)
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 的強引用躁锡,該實例被釋放了。其后毒租,再也沒有指向 CreditCard 實例的強引用稚铣,該實例也隨之被釋放了
john = nil
// prints "John Appleseed is being deinitialized"
// prints "Card #1234567890123456 is being reinitialized"
無主引用和隱式展開的可選屬性
//上面弱引用和無主引用例子涵蓋了兩種常用的需要打破循環(huán)強引用的場景:
- Person 和 Apartment 的例子展示了兩個屬性的值都允許為 nil 箱叁,并會潛在的產(chǎn)生循環(huán)強引用。這種場景最適合用弱引用來解決惕医。
- Customer 和 CreditCard 的例子展示了一個屬性的值允許為 nil 耕漱,而另一個屬性的值不允許為 nil ,這也可能導致循環(huán)強引用抬伺。這種場景最好使用無主引用來解決螟够。
- 還有第三種場景,在這種場景中峡钓,兩個屬性都必須有值妓笙,并且初始化完成后永遠不會為 nil 。在這種場景中能岩,需要一個類使用無主屬性寞宫,而另外一個類使用隱式展開的可選屬性。
// Country 和 City 拉鹃,每個類將另外一個類的實例保存為屬性辈赋。在這個數(shù)據(jù)模型中,每個國家必須有首都膏燕,每個城市必須屬于一個國家钥屈。為了實現(xiàn)這種關(guān)系, Country 類擁有一個 capitalCity 屬性坝辫,而 City 類有一個 country 屬性:(都不可nil)
class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// prints "Canada's capital city is called Ottawa"
閉包的循環(huán)強引用(block的循環(huán)飲用)
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: Void -> 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())
// prints "<h1>some default text</h1>"
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// prints"hello, world"
// 解決 這句話篷就? [unowned self, weak delegate = self.delegate!] 放在閉包的參數(shù)返回值之前
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// prints"hello, world"
lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
// 解決
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: Void -> 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())
// prints "<p>hello, world</p>"
paragraph = nil
// prints "p is being deinitialized"