閉包只有在函數(shù)中做參數(shù)時才會區(qū)分逃逸閉包和非逃逸閉包趣钱。
Swift 3.0之后涌献,傳遞閉包到函數(shù)中的時候,系統(tǒng)會默認為非逃逸閉包類型(NonescapingClosures)@noescaping首有,逃逸閉包在閉包前要添加@escaping關(guān)鍵字燕垃。
從生命周期看兩者區(qū)別:
- 非逃逸閉包的生命周期與函數(shù)相同:
1,把閉包作為參數(shù)傳給函數(shù)井联;
2卜壕,函數(shù)中調(diào)用閉包;
3烙常,退出函數(shù)轴捎。結(jié)束
- 逃逸閉包的生命周期:
1,閉包作為參數(shù)傳遞給函數(shù)蚕脏;
2侦副,退出函數(shù);
3驼鞭,閉包被調(diào)用跃洛,閉包生命周期結(jié)束
即逃逸閉包的生命周期長于函數(shù),函數(shù)退出的時候终议,逃逸閉包的引用仍被其他對象持有汇竭,不會在函數(shù)結(jié)束時釋放
例如:
非逃逸閉包:
class ToolClass:NSObject{
func test(testBlock:(String)->()) {//1
testBlock("非逃逸閉包")//2
}//3
}
class ViewController: UIViewController {
var tool:ToolClass = ToolClass.init()
override func viewDidLoad() {
super.viewDidLoad()
tool.test { (str) in
print(str)
}
}
}
代碼執(zhí)行順序(1)葱蝗,(2),(3)
當傳遞閉包參數(shù)給函數(shù)test時细燎,要注意ViewController中的屬性tool两曼,雖然閉包會捕獲self,但是由于默認閉包參數(shù)是非逃逸型玻驻,這里可以省略self悼凑,編譯器已經(jīng)知道這里不會有循環(huán)引用的潛在風險。
逃逸閉包:
class ToolClass:NSObject{
func test2(testBlock2:@escaping(String)->()) {//1
DispatchQueue.global().async {
DispatchQueue.main.async {
testBlock("逃逸閉包")//2
}
}
}//3
}
class ViewController: UIViewController {
var tool:ToolClass = ToolClass.init()
override func viewDidLoad() {
super.viewDidLoad()
tool.test2 { (str2) in
print(str2)
}
}
}
代碼執(zhí)行順序:(1)璧瞬,(3)户辫,(2)
當傳遞閉包參數(shù)給函數(shù)test2時,要注意ViewController中的屬性tool嗤锉,這里閉包函數(shù)的生命周期在函數(shù)結(jié)束后結(jié)束渔欢,tool前面省略的self 就有必要做特殊處理,防止造成循環(huán)引用weak var weakSelf = self
瘟忱。逃逸閉包前面添加@escaping關(guān)鍵字奥额,這里閉包的生命周期不可預知。
經(jīng)常使用逃逸閉包的2個場景:
- 1.異步調(diào)用: 如果需要調(diào)度隊列中異步調(diào)用閉包访诱,比如網(wǎng)絡請求成功的回調(diào)和失敗的回調(diào)垫挨,這個隊列會持有閉包的引用,至于什么時候調(diào)用閉包触菜,或閉包什么時候運行結(jié)束都是不確定九榔,上邊的例子。
- 2.存儲: 需要存儲閉包作為屬性涡相,全局變量或其他類型做稍后使用哲泊,例如
let kscreenWidth = UIScreen.main.bounds.size.width
let kscreenHeight = UIScreen.main.bounds.size.height
@objcMembers class AdvertiseView: UIView {
private var dismisBlock: (() -> Void)?
private var downBlock: (() -> Void)?
private var completion: (() -> Void)?
init(frame: CGRect, dismis: @escaping () -> Void, down: @escaping () -> Void, completion: @escaping () -> Void) {
super.init(frame: frame)
dismisBlock = dismis
downBlock = down
self.completion = completion
}
func test(){
if (self.completion != nil) {
self.completion!()
}
}
}
swift中可以通過三種方法解決循環(huán)引用的問題
- 利用類似oc方法解決循環(huán)引用weak var weakSelf = self
weak var weakSelf = self
loadData = { (value) in
print(weakSelf.xxx)
}
- [weak self]推薦使用
loadData = { [weak self] (value) in
print(self.xxx)
}
- [unowned self]不推薦使用
loadData = {[unowned self] (value) in
print(self.xxx)
}
解決循環(huán)引用實際場景示例:
class ToolClass:NSObject{
func test2(testBlock2:@escaping(String)->()) {//1
DispatchQueue.global().async {
DispatchQueue.main.async {
testBlock("逃逸閉包")//2
}
}
}//3
}
class ViewController: UIViewController {
var tool:ToolClass = ToolClass.init()
var titleName:String?
override func viewDidLoad() {
super.viewDidLoad()
self.titleName = "a"
//解決循環(huán)引用 方法一 類OC做法
weak var weakSelf = self
tool.test2 { (str2) in
print(str2)
print(weakSelf?.titleName)
}
//解決循環(huán)引用 方法二 推薦做法
tool.test2 {[weak self] (str2) in
print(str2)
print(self?.titleName)
}
//解決循環(huán)引用 方法一 不推薦做法
tool.test2 {[unowned self] (str2) in
print(str2)
print(self?.titleName)
}
}
}