上篇回顾
上一篇我们了解到了闭包的定义、闭包表达式、尾随闭包以及闭包的本质,本篇我们再继续来探讨闭包。
1、OC-Block与Swift-闭包相互调用
OC与Swift是可以混合开发的,这一点我们是清楚的,那在OC中Block与Swift中的闭包是怎么相互调用的呢,下面我们来看一下:
//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 语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是
先出现的后执行。