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 是真香。
 
  