/*
? 自動引用計數(shù)的工作機制
? 自動引用計數(shù)實踐
? 類實例之間的循環(huán)強引用
? 解決實例之間的循環(huán)強引用
? 閉包引起的循環(huán)強引用
? 解決閉包引起的循環(huán)強引用
*/
//自動引用計數(shù)器的工作機制
/*當你每次創(chuàng)建一個類的新的實例的時候,ARC 會分配一塊內存來儲存該實例信息脱羡。內存中會包含實例的類型信 息,以及這個實例所有相關的存儲型屬性的值免都。
此外,當實例不再被使用時,ARC 釋放實例所占用的內存,并讓釋放的內存能挪作他用。這確保了不再被使用的 實例,不會一直占用內存空間脓规。
然而,當 ARC 收回和釋放了正在被使用中的實例,該實例的屬性和方法將不能再被訪問和調用业舍。實際上,如果你 試圖訪問這個實例,你的應用程序很可能會崩潰。
為了確保使用中的實例不會被銷毀,ARC 會跟蹤和計算每一個實例正在被多少屬性,常量和變量所引用态罪。哪怕實 例的引用數(shù)為1,ARC都不會銷毀這個實例。
為了使上述成為可能,無論你將實例賦值給屬性复颈、常量或變量,它們都會創(chuàng)建此實例的強引用耗啦。之所以稱之
為“強”引用,是因為它會將實例牢牢地保持住,只要強引用還在,實例是不允許被銷毀的。*/
//自動引用計數(shù)實踐
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
// Person 類有一個構造函數(shù),此構造函數(shù)為實例的 name 屬性賦值,并打印一條消息以表明初始化過程生效帜讲。 on 類也擁有一個析構函數(shù),這個析構函數(shù)會在實例被銷毀時打印一條消息椒拗。
var reference1: Person?
var reference2: Person?
var reference3: Person?
reference1 = Person(name: "John Appleseed")
// 應當注意到當你調用 Person 類的構造函數(shù)的時候, “John Appleseed is being initialized” 會被打印出來获黔。由 此可以確定構造函數(shù)被執(zhí)行在验。
reference2 = reference1
reference3 = reference1
//如果你通過給其中兩個變量賦值 nil 的方式斷開兩個強引用(包括最先的那個強引用),只留下一個強引用, rson 實例不會被銷毀:
reference1 = nil
reference2 = nil
//在你清楚地表明不再使用這個 Person 實例時,即第三個也就是最后一個強引用被斷開時,ARC 會銷毀它:
reference3 = nil
// 打印 “John Appleseed is being deinitialized”
//類實例之間的循環(huán)引用
//在上面的例子中,ARC 會跟蹤你所新創(chuàng)建的 Person 實例的引用數(shù)量,并且會在 Person 實例不再被需要時銷毀它腋舌。
class Person1 {
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:Person1?
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
// 不幸的是,這兩個實例關聯(lián)后會產生一個循環(huán)強引用。 Person 實例現(xiàn)在有了一個指向 Apartment 實例的強引 用,而 Apartment 實例也有了一個指向 Person 實例的強引用赞辩。因此,當你斷開 john 和 unit4A 變量所持有的強 引用時,引用計數(shù)并不會降為 0 ,實例也不會被 ARC 銷毀:
john = nil
unit4A = nil
//注意,當你把這兩個變量設為 nil 時,沒有任何一個析構函數(shù)被調用刨沦。循環(huán)強引用會一直阻止 Person 和 Apartme nt 類實例的銷毀,這就在你的應用程序中造成了內存泄漏。
//解決實例之間的循環(huán)引用
//弱引用:聲明屬性或者變量時,在前面加上 weak 關鍵字表明這是一個弱引用召庞。
// 然后跟之前一樣,建立兩個變量( john 和 unit4A )之間的強引用,并關聯(lián)兩個實例:
//無主引用
//和弱引用類似,無主引用不會牢牢保持住引用的實例篮灼。和弱引用不同的是,無主引用在其他實例有相同或者更長 的生命周期時使用。你可以在聲明屬性或者變量時,在前面加上關鍵字 unowned 表示這是一個無主引用徘禁。
//重要
// 使用無主引用,你必須確保引用始終指向一個未銷毀的實例送朱。
// 如果你試圖在實例被銷毀后,訪問該實例的無主引用,會觸發(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:NSInteger
unowned let customer: Customer
init(number:NSInteger , customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) is being deinitialized")
}
}
var john1: Customer?
john1 = Customer(name: "John Appleseed")
john1!.card = CreditCard(number: 1234_5678_9012_3456, customer: john1!)
//無主引用以及隱式解析可選屬性
//兩個屬性都必須有值,并且初始化完成后永遠不會為 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
}
}
//為了建立兩個類的依賴關系, City 的構造函數(shù)接受一個 Country 實例作為參數(shù),并且將實例保存到 country 屬性翔试。
//Country 的構造函數(shù)調用了 City 的構造函數(shù)复旬。然而,只有 Country 的實例完全初始化后, Country 的構造函數(shù) 才能把 self 傳給 City 的構造函數(shù)
//為了滿足這種需求,通過在類型結尾處加上感嘆號( City! )的方式,將 Country 的 capitalCity 屬性聲明為隱 式解析可選類型的屬性。這意味著像其他可選類型一樣, capitalCity 屬性的默認值為 nil ,但是不需要展開它 的值就能訪問它
// 由于 capitalCity 默認值為 nil ,一旦 Country 的實例在構造函數(shù)中給 name 屬性賦值后,整個初始化過程就完 成了驹碍。這意味著一旦 name 屬性被賦值后, Country 的構造函數(shù)就能引用并傳遞隱式的 self 。 Country 的構造函 數(shù)在賦值 capitalCity 時,就能將 self 作為參數(shù)傳遞給 City 的構造函數(shù)粹庞。
//以上的意義在于你可以通過一條語句同時創(chuàng)建 Country 和 City 的實例,而不產生循環(huán)強引用,并且 的屬性能被直接訪問,而不需要通過感嘆號來展開它的可選值:
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
//閉包引起的循環(huán)引用
//循環(huán)強引用還會發(fā)生在當你將一個閉包賦值給類實例的某個屬性,并且這個閉包體中又使用了這個類實例時庞溜。這個閉包體中可能訪問了實例的某個屬性,例如 self.someProperty ,或者閉包中調用了實例的某個方法,例如 self.someMethod() 碑定。這兩種情況都導致了閉包“捕獲” self ,從而產生了循環(huán)強引用延刘。
//循環(huán)強引用的產生,是因為閉包和類相似,都是引用類型。
//下面的例子為你展示了當一個閉包引用了 self 后是如何產生一個循環(huán)強引用的碘赖。例子中定義了一個叫 t 的類,用一種簡單的模型表示 HTML 文檔中的一個單獨的元素:
// 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")
// }
//
// }
//解決閉包引起的循環(huán)引用
//只要在閉包內使用 self 的成員,就要用 self.someProperty 或者 self.someMethod() (而 不只是 someProperty 或 someMethod() )普泡。這提醒你可能會一不小心就捕獲了 self 。
//定義捕獲列表
// 捕獲列表中的每一項都由一對元素組成,一個元素是 weak 或 unowned 關鍵字,另一個元素是類實例的引用(例如 self )或初始化過的變量(如 delegate = self.delegate! )歧匈。這些項在方括號中用逗號分開砰嘁。
//如果閉包有參數(shù)列表和返回類型,把捕獲列表放在它們前面:
//lazy var someClosure: (NSInteger ,String) -> String = {
// [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in // 這里是閉包的函數(shù)體
// }
//如果閉包沒有指明參數(shù)列表或者返回類型,即它們會通過上下文推斷,那么可以把捕獲列表和關鍵字 in 放在閉包 最開始的地方:
//lazy var someClosure: Void -> String = {
// [unowned self, weak delegate = self.delegate!] in // 這里是閉包的函數(shù)體
//}
//弱引用和無主引用
//在閉包和捕獲的實例總是互相引用并且總是同時銷毀時,將閉包內的捕獲定義為 無主引用 。
//相反的,在被捕獲的引用可能會變?yōu)?nil 時,將閉包內的捕獲定義為 弱引用 斟冕。弱引用總是可選類型,并且當引用 的實例被銷毀后,弱引用的值會自動置為 nil 板祝。這使我們可以在閉包體內檢查它們是否存在。
//前面的 HTMLElement 例子中,無主引/弱引用用是正確的解決循環(huán)強引用的方法孤里。這樣編寫 HTMLElement 類來避免循環(huán)強 引用:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: (Void) -> String = {
[unowned self] in//[weak 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")
}
}
swift-自動引用計數(shù)器
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
- 文/潘曉璐 我一進店門拜效,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拂檩,“玉大人嘲碧,你說我怎么就攤上這事⊥椋” “怎么了履婉?”我有些...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饥努,像睡著了一般八回。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溶浴,一...
- 文/蒼蘭香墨 我猛地睜開眼囚戚,長吁一口氣:“原來是場噩夢啊……” “哼狞洋!你這毒婦竟也來了?” 一聲冷哼從身側響起庐橙,我...
- 正文 年R本政府宣布但惶,位于F島的核電站,受9級特大地震影響膀曾,放射性物質發(fā)生泄漏。R本人自食惡果不足惜财喳,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扎瓶。 院中可真熱鬧泌枪,春花似錦、人聲如沸误证。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽慈鸠。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稽亏,已是汗流浹背。 一陣腳步聲響...
推薦閱讀更多精彩內容
- 參考Swift Program Language 基礎知識 對象分為三種引用類方式 分別為 strong weak...
- swift使用自動引用計數(shù)(ARC)機制來跟蹤和管理應用程序的內存。一般情況下,swift內存管理機制會一直起作用...
- 本章將會介紹 自動引用計數(shù)的工作機制自動引用計數(shù)實踐類實例之間的循環(huán)強引用解決實例之間的循環(huán)強引用閉包引起的循環(huán)強...
- Swift 使用自動引用計數(shù)(ARC)這一機制來跟蹤和管理應用程序的內存通常情況下我們不需要去手動釋放內存业踢,因為 ...
- 自動引用計數(shù)(Automatic Reference Counting) Swift 使用自動引用計數(shù)(ARC)機...