原文
一、 一行代碼執行 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
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。