闭包只有在函数中做参数时才会区分逃逸闭包和非逃逸闭包。
Swift 3.0之后,传递闭包到函数中的时候,系统会默认为非逃逸闭包类型(NonescapingClosures)@noescaping,逃逸闭包在闭包前要添加@escaping关键字。
从生命周期看两者区别:
- 非逃逸闭包的生命周期与函数相同:
1,把闭包作为参数传给函数;
2,函数中调用闭包;
3,退出函数。结束
- 逃逸闭包的生命周期:
1,闭包作为参数传递给函数;
2,退出函数;
3,闭包被调用,闭包生命周期结束
即逃逸闭包的生命周期长于函数,函数退出的时候,逃逸闭包的引用仍被其他对象持有,不会在函数结束时释放
例如:
非逃逸闭包:
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)
}
}
}
代码执行顺序(1),(2),(3)
当传递闭包参数给函数test时,要注意ViewController中的属性tool,虽然闭包会捕获self,但是由于默认闭包参数是非逃逸型,这里可以省略self,编译器已经知道这里不会有循环引用的潜在风险。
逃逸闭包:
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)
}
}
}
代码执行顺序:(1),(3),(2)
当传递闭包参数给函数test2时,要注意ViewController中的属性tool,这里闭包函数的生命周期在函数结束后结束,tool前面省略的self 就有必要做特殊处理,防止造成循环引用weak var weakSelf = self
。逃逸闭包前面添加@escaping关键字,这里闭包的生命周期不可预知。
经常使用逃逸闭包的2个场景:
- 1.异步调用: 如果需要调度队列中异步调用闭包,比如网络请求成功的回调和失败的回调,这个队列会持有闭包的引用,至于什么时候调用闭包,或闭包什么时候运行结束都是不确定,上边的例子。
- 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中可以通过三种方法解决循环引用的问题
- 利用类似oc方法解决循环引用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)
}
解决循环引用实际场景示例:
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"
//解决循环引用 方法一 类OC做法
weak var weakSelf = self
tool.test2 { (str2) in
print(str2)
print(weakSelf?.titleName)
}
//解决循环引用 方法二 推荐做法
tool.test2 {[weak self] (str2) in
print(str2)
print(self?.titleName)
}
//解决循环引用 方法一 不推荐做法
tool.test2 {[unowned self] (str2) in
print(str2)
print(self?.titleName)
}
}
}
网友评论