Swift-闭包(Closure) 下

529 阅读4分钟

上篇回顾

swift-闭包(上)

上一篇我们了解到了闭包的定义闭包表达式尾随闭包以及闭包的本质,本篇我们再继续来探讨闭包

1、OC-Block与Swift-闭包相互调用

OCSwift是可以混合开发的,这一点我们是清楚的,那在OCBlockSwift中的闭包是怎么相互调用的呢,下面我们来看一下:

//oc  .h文件 定义一个方法,Block作为参数
+(void)testBlock:(void(^)(NSInteger index))block;

//oc  .m文件 实现方法,调用Block
+(void)testBlock:(void(^)(NSInteger index))block {
    if (block) {
        block(10);
    }
}

//swift中调用test方法
Test.test { index in
    print("oc-block传过来的值\(index)")
}


//输出
oc-block传过来的值10

Swift是怎么调用的OC的方法呢,我们通过以下步骤看一下

可以看到,实际上编译器是把OC中的方法编译成了Swift的方法,所以Swift就可以调用了。

Swift中调用OC中的Block还是比较简单的。

OC是怎么调用Swift中的闭包的呢,我们通过以下代码看一下

//在Swift中定义一个闭包
class XLTest: NSObject {
    @objc static var closure: ((_ name: String) -> Void)?
}

//先调用OC中的方法,建立联系Test.getName { name in
    print("oc-swift交互的值\(name)")
}
//再调用闭包
XLTest.closure?("哈哈")


//在OC .h文件中定义一个方法(由于主要探讨的Swift内的闭包,所以这里需要把传过来的name再传回给Swift来输出)
+(void)getName:(void(^)(NSString *name))block;
//在OC .m文件中实现方法
+(void)getName:(void(^)(NSString *name))block{
    
    XLTest.closure = ^(NSString * _Nonnull name) {
        NSLog(@"Swift传过来的值 %@",name);
        block(name);
    };
}

//输出
Swift传过来的值 哈哈oc-swift交互的值 哈哈

OC是怎么调用的Swift的方法呢,我们也通过步骤看一下

可以看到,实际上编译器是把Swift中的方法或属性编译成了OC的方法,所以Swift就可以调用了,当然前提是这个类要继承自NSObject并且需要加 @objc关键字暴露给OC调用

还有需要注意的一点是 @convention这个关键字:用于修饰函数类型

  •  修饰Swift中的函数类型(调用 C 函数的时候) 

    let closure: @convention(c)(Int32, Int32) -> Int32 = {(a:Int32, b: Int32) -> Int32 in return a+b }

  •  调用 OC 方法时,修饰 Swift 函数类型 

2、逃逸闭包

闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回之后调用,我们就说这个闭包逃逸了。当我们声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。

  • 作为函数参数传递
  • 当前闭包函数内部异步执行或者被存储
  • 函数结束,闭包被调用,生命周期结束

此时编译器就会提示我们,这是一个逃逸闭包,需要加上@escaping关键字描述

3、自动闭包@autoclosure

自动闭包@autoclosure是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// 打印出“5”

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出“5”

print("Now serving \(customerProvider())!")
// 打印出“Now serving Chris!”
print(customersInLine.count)
// 打印出“4”

将以上代码修改成以下这样,同样可以获得延时求值的行为

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )

这个版本的代码接受一个显式的闭包,如果用自动闭包来实现的话,它就不用接受一个显式的闭包,而是通过将参数标记为 @autoclosure 来接收一个自动闭包。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))


  • 过度使用 autoclosures 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。

  • 逃逸闭包可以和自动闭包同时使用

4、defer

defer {} 里的代码会在当前代码块返回的时候执行,无论当前代码块是从哪个分支

return 的,即使程序抛出错误,也会执行。

func testDefer() {
    defer {
        print("First defer")
    }
    defer {
        print("Second defer")
    }
    
    print("end of testDefer")
}
testDefer()

输出顺序
end of testDefer --> Second defer -> First defer

如果多个 defer 语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是

先出现的后执行。