本人有若干成套學(xué)習(xí)視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含Java
, 數(shù)據(jù)結(jié)構(gòu)與算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 聯(lián)系微信tsaievan
.
Swift在閉包的使用過程中有可能會(huì)引起循環(huán)引用, 這和OC中的循環(huán)引用是類似的.
現(xiàn)在假設(shè)我有兩個(gè)控制器, 點(diǎn)擊第一個(gè)控制器的跳轉(zhuǎn)會(huì)跳轉(zhuǎn)到第二個(gè)控制器, 第二個(gè)控制器pop回來的時(shí)候會(huì)deinit
控制器的簡(jiǎn)單跳轉(zhuǎn)
我現(xiàn)在僅僅在B控制器里面寫如下代碼:
- 先定義一個(gè)閉包屬性
//---------1. 先定義一個(gè)閉包屬性 ---------
/* 這個(gè)閉包有一個(gè)String類型的參數(shù), 無返回值 */
var finishedCallBack:((_ str: String) -> ())?
- 定義一個(gè)函數(shù), 函數(shù)的參數(shù)為跟閉包屬性類型相同的參數(shù), 然后再把閉包參數(shù)賦值給屬性
func demo(finished: @escaping (_ str: String) -> ()) {
self.finishedCallBack = finished
}
- 定義一個(gè)函數(shù), 函數(shù)內(nèi)部執(zhí)行這個(gè)閉包
func secondDemo() {
if let finishedHandler = self.finishedCallBack {
finishedHandler("secondDemo is running")
}
}
- 最后, 在viewDidLoad中分別執(zhí)行
demo()
和secondDemo()
兩個(gè)函數(shù)- 執(zhí)行
demo()
的時(shí)候是給閉包賦值 - 執(zhí)行
secondDemo()
的時(shí)候是調(diào)用閉包
- 執(zhí)行
override func viewDidLoad() {
super.viewDidLoad()
demo { (string) in
print("\(string)----\(self.view!)")
}
secondDemo()
}
如以上代碼所示:
我在定義這個(gè)閉包的時(shí)候, 閉包內(nèi)部引用了
self
, 那么閉包就對(duì)self進(jìn)行了一個(gè)copy操作, 即閉包持有控制器這個(gè)閉包又賦值給了閉包屬性, 所以閉包屬性對(duì)控制器有一個(gè)強(qiáng)引用
閉包屬性又被控制器強(qiáng)引用, 所以他們之間構(gòu)成了一個(gè)循環(huán)引用
如下圖所示:
閉包的循環(huán)引用
這個(gè)時(shí)候, 我在第二個(gè)控制器的頁(yè)面pop回去的時(shí)候, deinit方法不會(huì)調(diào)用:
看控制臺(tái)的打印:
循環(huán)引用后控制臺(tái)的打印結(jié)果
如何解除循環(huán)引用呢?
下面提供三個(gè)方法:
- 方法一: OC的方法, 在OC中,可以是有
__weak typeof(self)weakSelf = self;
在Swift中, 也有類似的方法:
override func viewDidLoad() {
super.viewDidLoad()
/* 方法一, OC的方法 */
weak var weakSelf = self
demo { (string) in
if let wSelf = weakSelf {
print("\(string)----\(wSelf.view)")
}
}
secondDemo()
}
Swift中就是weak var weakSelf = self
這句代碼
- 方法二: [weak self]修飾閉包
override func viewDidLoad() {
super.viewDidLoad()
/* 方法二: [weak self]修飾閉包 */
demo {[weak self] (string) in
if let weakSelf = self {
print("\(string)----\(weakSelf.view)")
}
}
secondDemo()
}
- 方法三: [unowned self]修飾閉包
super.viewDidLoad()
/* 方法三: [unowned self]修飾閉包 */
demo {[unowned self] (string) in
print("\(string)----\(self.view)")
}
secondDemo()
}
注意: 方法二和方法三的區(qū)別在于, 當(dāng)方法二中的控制器被銷毀時(shí), self指針會(huì)指向nil, 而當(dāng)方法三中的控制器被銷毀時(shí), self指針是不會(huì)指向nil的,這時(shí)候就會(huì)形成野指針, 因此, 第三種方法解除循環(huán)引用是不推薦的, 有可能會(huì)引起一些問題
解除循環(huán)引用之后, 控制臺(tái)的打印顯示: deinit方法被調(diào)用了
解除循環(huán)引用后的控制臺(tái)打印