Swift 闭包(block)详解

1、闭包

Swift对闭包进行了简化:

  • 利用上下文推断参数和返回值类型
  • 隐式返回单表达式闭包,即单表达式闭包可以省略return关键字
  • 参数名称缩写
  • 尾随(Trailing)闭包语法

先来看一个排序的例子,数组的降序排列

let usernames = ["Wangwu", "Lisi", "Xiaoming", "Zhangsan"]
func backWards(s1: String, s2: String) -> Bool
{
    return s1 > s2
}
let resultName1 = usernames.sorted(by: backWards)
//resultName1: ["Zhangsan", "Xiaoming", "Wangwu", "Lisi"]

 1.1 闭包表达式语法

{ (parameters) -> returnType in
      statements
}

1.2 单表达式闭包隐式返回

单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果

let resultName2 = usernames.sorted { s1, s2 in s1 > s2 }

1.3 参数名称缩写

let resultName3 = usernames.sorted { $0 > $1 }

1.4 函数式闭包 

let resultName4 = usernames.sorted(by: >)

2. 捕获值(Capturing Values)

闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

3.闭包是引用类型(Closures Are Reference Types)

和类一样,必要也是引用类型

4. 尾随闭包(Trailing Closures))

尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:

        let numReult2 = caculateTwoNumbers(num1: 3, num2: 4) {  $0 * $1 }
        print(numReult2)
    
    func caculateTwoNumbers(num1: Int, num2: Int, CaluFunction: (Int, Int) -> Int) -> Int{
        return CaluFunction(num1, num2)
    }

5. 逃逸闭包(@escaping)

    func mainFunc(){
        //调用函数
        doSomething(paramClosure: {print("hello")})
        doSomething(paramClosure:{print("word!")})
        //逃逸调用闭包
        for closurePrama in functionArray {
            closurePrama()
        }
        //非逃逸闭包
        someFunctionWithNonescapingClosure { (a) in
            print(a)
        }
    }
    //声明一个存放函数的数组
    var functionArray: [() -> Void] = []
    //定义一个接收闭包参数的函数,如果定义非逃逸函数 func doSomething(@noescape paramClosure:() -> Void) 就会编译错误
    func doSomething(paramClosure:@escaping  () -> Void){
        //把参数放入数组中,用于逃逸调用
        functionArray.append(paramClosure)
    }
    //非逃逸闭包 默认@noescape 可以省略不写
    func someFunctionWithNonescapingClosure(closure: (_ num:Int) -> Void) {
        let a = 1
        closure(a)
    }

6、noescape是非逃逸的意思。

@noescape关键字代码中扮演了一个标注的作用:来说明一个闭包参数,该闭包参数与此API是同步的,它只在此API中被调用。只要该API运行结束,该闭包的生命周期就结束。也就是说,该闭包逃不出该API的手掌心。哈哈哈哈!它对编译器和API调用者来说:编译器会对代码做一些优化,而API调用者则可以放心大胆的使用该API,不用因为担心造成引用循环而去使用捕获列表。同时在其中调用实例变量或实例方法的时候可以不使用"self."

      但是!如何使用这个@noescape标注,这是需要正确的姿势的!

      上面的论述,只有在闭包是临时创建,即没有被API外部的任何其他属性或全局变量持有的前提下才成立!!

func withLock(@noescape perform closure: () -> Void) {
    myLock.lock()
    closure()
    myLock.unlock()
}

In  Objective-C

- (void)performWithLock:(__attribute__((noescape)) void (^)())block {  // exposed as @noescape to Swift
    [myLock lock];
    block();
    [myLock unlock];
}

面试题:调用Masonry的block为何不用weak?

原因就是使用了栈block,都是用NS_NOESCAPE修饰block.编译器会相应地做一些优化,例如去掉一些多余的对self的捕获、retain、release操作。

- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

 

你可能感兴趣的:(swift,block)