原文
一、 一行代碼執行 block 的安全調用
基於 判斷對象是否為 nil 的處理,obj ?: someDefault 的思路,調整 block 的調用
void(^block)(void) = ...; !block ?: block(); // 安全調用
PS: 如果是 Swift 的可選類型閉包,則可利用可選鏈處理
var closure: (() -> Void)? = ... closure?() // 可選鏈調用
二、利用系統現成的 typedef 的相關 block
比如 GCD 中關於無參數無返回值的 typedef
typedef void (^dispatch_block_t)(void);
這樣就可以很愉快地利用 dispatch_block_t 來聲明無參數和無返回值的 block 了,而在 UIKit 中有很多 block 參數都是無參數無返回值類型 block,在實際業務中也可以利用,比如 上一篇文章中末尾的應用 注意自定義 AlertController 的回調時機。
Swift 中的此用法暫時沒有找到很合適的,可以從一個協議中看到類似的定義
DispatchSourceProtocol.DispatchSourceHandler
extension DispatchSourceProtocol {
public typealias DispatchSourceHandler = @convention(block) () -> Swift.Void
....
}1更多的,繼續挖掘學習。
三、使用 block 作為函數/方法多個返回值的方案
在 OC 中一個方法返回多個參數有一些方案,比如使用指針的指針(類似返回 NSError 的用法),或者聲明字典、模型或者自定義一個 元組Tuple 類等。再者,就是使用 block 了,通過傳遞 block 將返回值取出的方式,舉例如下:
typedef NSInteger Int;
- (void)doSomeCalculate {
Int a = 1;
Int b = 2;
__block Int sumR = 0;
__block Int minR = 0;
__block Int maxR = 0;
1
[self calculateA:a b:b result:^(Int sum, Int min, Int max) {
sumR = sum;
minR = min;
maxR = maxR;
}];
1
NSLog(@"sum %zd min %zd max %zd", sumR, minR, maxR);
}
- (void)calculateA:(NSInteger)a
b:(NSInteger)b
result:(void(^)(Int sum, Int min, Int max))resultBlock {
if (!resultBlock) return;
1
resultBlock(a + b, MIN(a, b), MAX(a, b));
1
return [self doNothingButPrint];
}存在使用了類的成員變量而導致循環引用的情況,需要使用 weak- strong-dance,並在訪問實例變量使用結構體訪問成員的操作符 ->
@interface SomeClass : NSObject
@property (nonatomic, copy) dispatch_block_t block;
@end
typedef NSInteger Int;
@implementation SomeClass
{
Int _sum;
}
- (void)changeIvar {
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(SomeClass *) strongSelf = weakSelf;
if (!strongSelf) return;
strongSelf->_sum = 1024;
};
}
@end四、使用返回 對象本身的 block 實現鏈式語法
經典的例子是 OC 的自動布局第三方框架 Masonry 的使用,
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];其內部實現的主要思路是兩個
聲明一個只讀 block 屬性供外部調用,此 block 屬性返回實例本身
聲明一個屬性,調用後返回一個實例(可以是本身),並可以執行額外操作
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}不難看出,在一定程度上說,方法都可以轉換成 block 的形式調用,而 block 連續返回實例本身,從而可以實現鏈式語法調用,提高代碼的可讀性,嘗試實現一個鏈式加法調用如下:
@interface Dot : NSObject
@property (nonatomic, readonly) Dot *then;
@property (nonatomic, readonly) Dot *(^add)(NSInteger a);
- (NSInteger)getCurrent;
@end
@interface Dot ()
{
NSInteger _current;
}
@end
@implementation Dot
- (NSInteger)getCurrent {
return _current;
}
- (Dot *)then { return self; };
- (Dot *(^)(NSInteger))add {
return ^Dot *(NSInteger a){
_current += a;
return self;
};
}
- (void)dealloc {
NSLog(@"dot dealloc %@", self);
}
@end
/// 調用
Dot *dot = [[Dot alloc] init];
dot.add(1).then.add(2).then.add(3);
NSLog(@"now -> %zd", [dot getCurrent]); // 打印出 6
dot = nil;
1使用鏈式語法有一個很大的前提是調用 block 前該 block 不能為空,所以使用時注意挑選調用者不為空的場景,參考 Masonry。
再者,鏈式語法在構造構造時有很好的優勢,比如純代碼構造 UI 控件,推薦閱讀以下 LinkBlock, 從而這樣寫一個 Label
UILabelNew .labText(@"UILable").labNumberOfLines(0).labAlignment(NSTextAlignmentCenter) .viewSetFrame(20,200,150,80) .viewBGColor(@"#CCCCCC".strToUIColorFromHex()) .viewAddToView(self.view);
此外,block 在很多開源項目中都有很好的實踐,推薦:
ReactiveCocoa的 OC 版本
BlocksKit
誠邀你來討論我的專題, QQ群 287698622
作者:席萍萍Brook_iOS深圳
鏈接:http://www.jianshu.com/p/6189109859d6
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。