美文网首页
逃逸(escaping)/非逃逸(noescape)闭包清单

逃逸(escaping)/非逃逸(noescape)闭包清单

作者: tdt | 来源:发表于2017-11-24 16:55 被阅读24次

    本文是自己的加深理解和记忆的笔记,非原创。按照自己的理解习惯改写了其他文章的内容(引用资料在最下方),看看就好,最后强调一次,非原创。

    [TOC]

    1. 什么是逃逸闭包?如何标记?

    A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

    即:作为一个传入参数,若该闭包在函数返回后才被执行的话,则该闭包就是在逃逸函数。(这样的闭包就是逃逸闭包。)你需要在参数前加上@escaping标记来表明闭包是逃逸的。

    2. 什么情况下使用逃逸闭包标记?

    • 函数外存储

      如果一个函数参数可能导致引用循环,那么它需要被显示地标记出来。@escaping标记可以作为一个警告,来提醒使用这个函数的开发者注意引用关系。

      举个例子。此时的callbackself所持有,典型的可能在函数return之后被执行。

      class SomeClass {
          var callback:(()->Void)?
          func doSomething(callback:@escaping ()->Void) { // 加上逃逸修饰词
              self.callback = callback  
         }
      }
      
    • 异步调用

      同理,如果闭包被放进async dispatch queue,则该闭包也会被queue retain,同样可能在函式结束后才被执行,因此也算是“逃逸”

      举个例子。此时的callback被异步调用了

      class SomeClass {
          func doWorkAsync(block: @escaping () -> ()) { // 加上逃逸修饰词
              DispatchQueue.main.async {
                  block() 
              }
          }
      }
      

    3. 非逃逸闭包有什么限制

    • 不能在函式外储存

    • 不能进async dispatch queue

    • 不能作为其他逃逸闭包函数的参数

      把@noescape闭包传到其他@noescape参数是可以的,一连串不会逃逸的传值,最终还是不会逃逸(下面的@noescape会被编译器提示删除,因为swift3开始默认的就是非逃逸闭包)

      class SomeClass {
          func foo( code:@noescape (() -> String)) -> String {
              return bar(code: code)
          }
          func bar( code:@noescape (() -> String)) -> String {
              return code()
          }
      }
      

    4.其他:

    • 从swift3开始,闭包默认为非逃逸闭包。之前则相反,且使用@noescape进行标记(此标记已废弃)。

    • 非逃逸闭包可用被编译器高度优化,快速的执行路径将被作为基准而使用,除非你在有需要的时候显式地使用其他方法。

    • 和弱引用关系:非逃逸闭包中可放心使用self关键字,因为不会在函数外储存,也不会被异步调用。你不需要去使用一个弱引用(weak或unowned)去引用self。

    • 在函数内部储存闭包也会被识别成逃逸,虽然并不会(现在的最新swift4仍存在这个问题)

      func doSomething(callback:(()->Void) {
          let c = callback // error: non-escaping parameter 'callback' may only be called
          c()
      }
      

    引用资料:

    相关文章

      网友评论

          本文标题:逃逸(escaping)/非逃逸(noescape)闭包清单

          本文链接:https://www.haomeiwen.com/subject/wcefbxtx.html