- Block 与 Method 最大的区别在于,Block 能够捕获上下文中的变量;
- Block 能够捕获上下文中的变量是有代价的,而这个代价就是:
- Block 本质上是对象,alloc release 会有额外开销;
- Block 在使用中,堆 Block 容易引起循环引用问题;栈 Block 容易引起 SIGSEGV;
因为堆 Block 容易引起循环引用问题,所以 UIKit 中使用的是 Action 模式、Delegate 模式等。
因为栈 Block 在非主线程中,容易遇到 SIGSEGV 问题,所以常规方法 封装方法
结论
- 在 ViewController - View 层面的接口设计中,应该避免使用 Block,而使用 Action 模式、Delegate 模式等常规 Callback 手段;
- 在 ViewModel - ViewController 层面的接口设计中,分为两种情况:
- ViewModel 是单例,接口需要捕获上下文中的变量。这种情况应该使用 Block, 简化开发,提高可维护性;
- ViewModel 不是单例,那么与 ViewController - View 的情况是类似的。无论是 ViewController 持有 ViewModel,还是 ViewModel 持有 ViewController,都容易引起循环引用问题,极大的增加开发难度。这种情况不应该使用 Block。
结论
今天又有了新的收获
- 如非必要,尽可能少的使用 Block
- 在 Block 中访问 self 的实例变量,必须显式声明,避免编译器语法糖导致的循环应用。
Block 中访问实例变量,不显式声明 weakSelf->,会引起循环引用
preferred:
^(NSString *title, NSString *url) {
NSLog(@"%@", weakSelf->testString)
};
not preferred:
^(NSString *title, NSString *url) {
NSLog(@"%@", testString)
};
使用 EXTScope,如果不显示声明 self->,也会引起循环引用
preferred:
@weakify(self)
^(NSString *title, NSString *url) {
@strongify(self);
NSLog(@"%@", self->testString)
};
not preferred:
@weakify(self)
^(NSString *title, NSString *url) {
@strongify(self);
NSLog(@"%@", testString)
};