Swift中使用(RAC)自動(dòng)引用計(jì)數(shù)機(jī)制,實(shí)例賦值給屬性、常量或變量,都會(huì)創(chuàng)建此實(shí)例的強(qiáng)引用,只要強(qiáng)引用存在,實(shí)例是不允許被銷毀的
實(shí)例間的循環(huán)強(qiáng)引用
當(dāng)兩個(gè)實(shí)例互相保持對(duì)方的強(qiáng)引用,并讓對(duì)方不會(huì)被銷毀,就會(huì)造成循環(huán)強(qiáng)引用。
例:
class School{
let classNum:Int
init(classNum:Int){
self.classNum = classNum
}
var student:Student?
deinit{
print("\(classNum)班,被析構(gòu)")
}
}
class Student {
var name:String = ""
init(name:String){
self.name = name
}
var school:School?
init(closure: @escaping (_ student: Student) -> Void) {
closure(self)
}
deinit{
print("\(name)" + "被析構(gòu)")
}
}
調(diào)用
//定義初始化為nil的屬性
var xiaoMing:Student?
var sunMiddleSchool:School?
func setValue(){
//賦值
xiaoMing = Student(name: "小明")
sunMiddleSchool = School(classNum: 3)
//對(duì)可選實(shí)例xiaoMing和sunMiddleSchool中的school、student屬性賦值(此時(shí)會(huì)造成循環(huán)強(qiáng)引用)
xiaoMing?.school = sunMiddleSchool
sunMiddleSchool?.student = xiaoMing
//釋放實(shí)例xiaoMing和sunMiddleSchool變量持有的強(qiáng)引用時(shí),引用計(jì)數(shù)不會(huì)降為0,實(shí)例也并不會(huì)被ARC銷毀,析構(gòu)函數(shù)也并不會(huì)被調(diào)用
//此時(shí)實(shí)例間的循環(huán)強(qiáng)引用阻止了實(shí)例銷毀,并會(huì)造成內(nèi)存泄漏
xiaoMing = nil
sunMiddleSchool = nil
}
解決實(shí)例間的循環(huán)強(qiáng)引用
Swift中提供了弱引用、無(wú)主引用兩種方法解決使用類的屬性遇到的循環(huán)強(qiáng)引用問(wèn)題
弱引用
對(duì)于生命周期中會(huì)變?yōu)閚il的實(shí)例使用弱引用,通過(guò)在定義類屬性語(yǔ)句前添加“weak”關(guān)鍵字聲明弱引用類屬性
class School{
let classNum:Int
init(classNum:Int){
self.classNum = classNum
}
//使用"weak"關(guān)鍵字定義弱引用實(shí)例屬性
weak var student:Student?
deinit{
print("\(classNum)班,被析構(gòu)")
}
}
class Student {
var name:String = ""
init(name:String){
self.name = name
}
//使用"weak"關(guān)鍵字定義弱引用實(shí)例屬性
weak var school:School?
init(closure: @escaping (_ student: Student) -> Void) {
closure(self)
}
deinit{
print("\(name)" + "被析構(gòu)")
}
}
調(diào)用
//定義初始化為nil的屬性
var xiaoMing:Student?
var sunMiddleSchool:School?
func setValue(){
//賦值
xiaoMing = Student(name: "小明")
sunMiddleSchool = School(classNum: 3)
//對(duì)可選實(shí)例xiaoMing和sunMiddleSchool中的school、student屬性賦值(此時(shí)形成的是循環(huán)弱引用)
xiaoMing?.school = sunMiddleSchool
sunMiddleSchool?.student = xiaoMing
//釋放實(shí)例xiaoMing和sunMiddleSchool變量持有的弱引用時(shí),引用計(jì)數(shù)降為0,實(shí)例會(huì)被ARC銷毀,析構(gòu)函數(shù)會(huì)被調(diào)用
xiaoMing = nil
sunMiddleSchool = ni
}
調(diào)用結(jié)果
小明被析構(gòu)
3班,被析構(gòu)
無(wú)主引用
對(duì)于初始化賦值后再也不會(huì)被賦值為nil的實(shí)例,使用無(wú)主引用。
閉包引起的循環(huán)強(qiáng)引用
例:
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")
}
}
調(diào)用
func HTMLElementFunc(){
var paragraph:HTMLElement? = HTMLElement(name: "p", text: "hello,world")
//HTMLElement 類產(chǎn)生了類實(shí)例和 asHTML 默認(rèn)值的閉包之間的循環(huán)強(qiáng)引用
print(paragraph!.asHTML())
paragraph = nil
}
調(diào)用結(jié)果
<p>hello,world</p>
從調(diào)用結(jié)果看出:析構(gòu)函數(shù)并未被調(diào)用說(shuō)明paragraph實(shí)例并沒有被釋放,實(shí)例的 asHTML 屬性持有閉包的強(qiáng)引用。但是,閉包在其閉包體內(nèi)使用了self(引用了self.name和self.text),因此閉包捕獲了self,這意味著閉包又反過(guò)來(lái)持有了HTMLElement實(shí)例的強(qiáng)引用。這樣兩個(gè)對(duì)象就產(chǎn)生了循環(huán)強(qiáng)引用.
解決閉包引起的循環(huán)強(qiáng)引用:在定義閉包時(shí)同時(shí)定義捕獲列表作為閉包的一部分,通過(guò)這種方式可以解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。
弱引用
1.當(dāng)閉包和捕獲的實(shí)例總是互相引用時(shí)并且總是同時(shí)銷毀時(shí),將閉包內(nèi)的捕獲定義為無(wú)主引用。
2.相反的,當(dāng)捕獲引用有時(shí)可能會(huì)是nil時(shí),將閉包內(nèi)的捕獲定義為弱引用。
3.如果捕獲的引用絕對(duì)不會(huì)置為nil,應(yīng)該用無(wú)主引用,而不是弱引用
無(wú)主引用
通過(guò)在定義閉包時(shí)同時(shí)定義捕獲列表[unowned self] in作為閉包的一部分,來(lái)解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。
class HTMLElement {
let name:String
let text:String?
lazy var asHTML:() -> String = {
C //定義閉包時(shí)同時(shí)定義捕獲列表
[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")
}
}
調(diào)用
func HTMLElementFunc(){
var paragraph:HTMLElement? = HTMLElement(name: "p", text: "hello,world")
print(paragraph!.asHTML())
paragraph = nil
}
調(diào)用結(jié)果
<p>hello,world</p>
p is being deinitialized
根據(jù)調(diào)用結(jié)果分析:此時(shí)的析構(gòu)函數(shù)被調(diào)用,實(shí)例paragraph已被釋放銷毀