Swift 使用闭包(block)
最近接触到个Swift写的项目,馋了很久的Swift终于有机会练手了,同事封装的组件回调使用了delegate,尝试把其改成了block回调。
如何使用闭包
声明闭包
// 声明闭包
typealias SwiftBlock = (String)->()赋值闭包
self.buttonBlock = {(text)->() in
print(text)
}调用闭包
@objc func onButton() {
print("onButton")
if buttonBlock != nil {
buttonBlock!("onButton - block")
}
}
Swift 闭包的 @escaping 和 @noescape
在OC里,闭包block及其容易造成循环引用,因为调用block不确定传入的block是否被持有,例如下面的代码:
- (void)func_A_WithBlock:(void(^)())block {
_block = block;
}
- (void)func_B_WithBlock:(void(^)())block {
block();
}
第一个方法是将block作为实例变量存入当前的对象。常见的例子是异步的网络请求回调。
第二个方法是立即调用这个传入来的block。常见的例子是数组的排序。
如果这是一个私有的类,@implementation看不到。那怎么判断这个block是拿来干什么的呢?
答案是无法判断。
第一个方法里的block是被当作实例变量接收了,例如该对象是A。对象A同时也被对象B持有,就成了这样B->A->block,这时block实现里引用了B,那么就变成了经典的B->A->block->B,引用循环。
如果我们不看内部实现,根本无法确切地判断出这个block是被对象A持有的。当然这是比较极端的例子,一般在声明方法时都会注明这个Block是作什么用的,只是在语言上无法防止这种不确定行为而已。
而Swift在闭包上加强了静态检查。它有两个修饰词@escaping和@noescape。这个看代码就能说明。
func addBlock(_ block: @escaping SwiftBlock) {
self.buttonBlock_2 = block
}
func doSomething(_ block: SwiftBlock) {
block("doSomething")
}
第一个方法加了@escaping,以为着“逃脱”,闭包的生命周期可以逃脱方法的作用域,在方法return后不会销毁,这意味着它的调用时机是不确定的,是异步的。一般用于异步网络请求。
第二个没有修饰词,所以是默认的@noescape,这意味着该闭包不能超出方法的作用域,方法return后闭包就销毁了,所以它是安全的。
下面这种做法是会报错的,因为在方法doSomething返回后,闭包还存在于异步队列里等候调用。
测试代码
import UIKit
// 声明闭包
typealias SwiftBlock = (String)->()
class ViewController: UIViewController {
public var buttonBlock : SwiftBlock? = nil
public var buttonBlock_2 : SwiftBlock? = nil
public var buttonBlock_3 : SwiftBlock? = nil
private let button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
button.frame = CGRect(x: 200,y: 200,width: 100,height: 40)
button.setTitle("button", for: UIControl.State.normal)
button.addTarget(self, action: #selector(onButton), for: .touchUpInside)
button.backgroundColor = UIColor.red
self.view.addSubview(button)
self.buttonBlock = {(text)->() in
print(text)
}
}
@objc func onButton() {
print("onButton")
if buttonBlock != nil {
buttonBlock!("onButton - block")
}
}
func addBlock(_ block: @escaping SwiftBlock) {
self.buttonBlock_2 = block
}
func doSomething(_ block: SwiftBlock) {
block("doSomething")
}
// func doSomething_2(_ block: SwiftBlock) {
// DispatchQueue(label: "queue").async {
// block("doSomething")
// }
// }
}
最后
Swift 是真香。