Swift 3必看:@noescape走了, @escaping来了

在学习Swift 3的过程中整理了一些笔记,如果想看其他相关文章可前往《Swift 3必看》系列目录

在之前,一个函数的参数的闭包的捕捉策略默认是escaping,如果是一个非逃逸闭包需要显示的添加声明@noescape。感兴趣的可以看我以前写过一篇介绍:Swift中被忽略的@noescape。简单的介绍就是如果这个闭包是在这个函数结束前内被调用,就是非逃逸的即noescape。如果这个闭包是在函数执行完后才被调用,调用的地方超过了这函数的范围,所以叫逃逸闭包。

举个例子就是我们常用的masonry或者snapkit的添加约束的方法就是非逃逸的。因为这闭包马上就执行了。

  public func snp_makeConstraints(file: String = #file, line: UInt = #line, @noescape closure: (make: ConstraintMaker) -> Void) -> Void {
        ConstraintMaker.makeConstraints(view: self, file: file, line: line, closure: closure)
    }

网络请求请求结束后的回调的闭包则是逃逸的,因为发起请求后过了一段时间后这个闭包才执行。比如这个Alamofire里的处理返回json的completionHandler闭包,就是逃逸的。

    public func responseJSON(
        queue queue: dispatch_queue_t? = nil,
        options: NSJSONReadingOptions = .AllowFragments,
        completionHandler: Response<AnyObject, NSError> -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: Request.JSONResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }

就像我之前写的那篇标题,很多人在写闭包参数的时候总是忽略去判断这个闭包是否是逃逸的。这对闭包的内存管理优化不太友好,都被当做了逃逸闭包处理。所以在3中做出了一个对调的改变:所有的闭包都默认为非逃逸闭包,不再需要@noescape;如果是逃逸闭包,就用@escaping表示。比如下面的一段代码,callBack在函数执行完后1秒才执行,所以是逃逸闭包。

func startRequest(callBack: ()->Void ) {
    DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) { 
        callBack()
    }
}

这样就需要显示的声明@escaping才能编译通过。


相关链接:
SE-0103:Make non-escaping closures the default

推荐阅读更多精彩内容