1.什么是循环引用
两个对象通过属性互相强引用对方,导致无法正常释放
情境代码
class City{
var country : Country?
deinit{
print("city is deinit")
}
}
class Country {
var capital : City?
deinit{
print("country is deinit")
}
}
不会循环引用
var city:City? = City()
var country:Country? = Country()
city!.country = country!
city = nil
country = nil
//city country此时释放会正常打印
//city is deinit
//country is deinit
会导致循环引用
var city2:City? = City()
var country2:Country? = Country()
city2!.country = country2!
country2!.capital=city2!
city2 = nil
country2 = nil
//city country此时设为nil,没有任何打印,说明并不会正常释放
2.在optional类型的属性中加上weak关键字
weak关键词不会使引用计数+1,同时如果所指向对象被释放,则自动设为nil
class City2{
weak var country : Country2?
deinit{
print("city2 is deinit")
}
}
class Country2 {
var capital : City2?
deinit{
print("country2 is deinit")
}
}
var city2:City2? = City2()
var country2:Country2? = Country2()
country2?.capital=city2!
city2?.country=country2
city2?.country == nil //false
country2 = nil //打印 country is deinit
city2?.country == nil //true
city2 = nil //打印 city2 is deinit
3.在非optional类型的属性中加上unown关键字
unown关键字只是不会使引用计数+1,所指向的对象被释放后,此属性不能再被访问,否则会报错
class City3{
var country : Country3?
deinit{
print("city3 is deinit")
}
}
class Country3 {
unowned var capital : City3
init(capital:City3){
self.capital = capital;
}
deinit{
print("country3 is deinit")
}
}
var city3:City3? = City3()
var country3:Country3? = Country3(capital:city3!)
city3?.country=country3
//打印 city2 is deinit
country3?.capital == nil //false
city3 = nil //打印 city3 is deinit
//country3?.capital == nil //报错
country3 = nil //打印 country3 is deinit
所以应该根据实际情况来决定是使用unowned关键字还是weak关键字
4.closure中的循环引用
class HTMLElement{
var text:String
init(text:String){
self.text=text
}
lazy var asHTML : Void -> String = {
return "<text>\(self.text)</text>"
}
deinit{
print("HTMLElement 被 释放了")
}
}
看起来并没有什么问题,编译器也没有报错
但是
var e :HTMLElement? = HTMLElement(text:"haha")
e?.asHTML()
e = nil //并没有任何打印,说明发生了循环引用
为什么会发生循环引用?
因为closure实际上也是一个对象,并且会捕获其所用到的变量。并默认使用一个强类型的指针,指向其用到的变量。这里发生的情况,如图
那么如何将其中的一个强类型指针改变成weak或者unowned呢?改变asHTML属性的类型为weak或者unowned吗?
显然不行,weak 和 unowned 只适用于class和protocol类型的属性
那只有修改closure中self的指针类型了
这样,在closure中加上[unowned self]的说明
class HTMLElement{
var text:String
init(text:String){
self.text=text
}
lazy var asHTML : Void -> String = { [unowned self] in
return "<text>\(self.text)</text>"
}
deinit{
print("HTMLElement 被 释放了")
}
}
如果是多个说明的话用逗号隔开比如
[unowned self,weak other]
这个说明应该放在参数列表的前面
lazy var asHTML : Void -> String = { [unowned self] () -> String in
return "<text>\(self.text)</text>"
}
加上说明再来测试一下
var e :HTMLElement? = HTMLElement(text:"haha")
e?.asHTML()
e = nil //打印HTMLElement 被 释放了
说明正常释放了