一、 一行代碼執(zhí)行 block 的安全調(diào)用
基于 判斷對(duì)象是否為 nil 的處理,obj ?: someDefault 的思路,調(diào)整 block 的調(diào)用
void(^block)(void) = ...;
!block ?: block(); // 安全調(diào)用
PS: 如果是 Swift 的可選類型閉包,則可利用可選鏈處理
var closure: (() -> Void)? = ...
closure?() // 可選鏈調(diào)用
二、利用系統(tǒng)現(xiàn)成的 typedef 的相關(guān) block
比如 GCD 中關(guān)于無參數(shù)無返回值的 typedef
typedef void (^dispatch_block_t)(void);
這樣就可以很愉快地利用 dispatch_block_t 來聲明無參數(shù)和無返回值的 block 了,而在 UIKit 中有很多 block 參數(shù)都是無參數(shù)無返回值類型 block,在實(shí)際業(yè)務(wù)中也可以利用,比如 上一篇文章中末尾的應(yīng)用 注意自定義 AlertController 的回調(diào)時(shí)機(jī)。
Swift 中的此用法暫時(shí)沒有找到很合適的,可以從一個(gè)協(xié)議中看到類似的定義
DispatchSourceProtocol.DispatchSourceHandler
extension DispatchSourceProtocol {
public typealias DispatchSourceHandler = @convention(block) () -> Swift.Void
....
}1
更多的,繼續(xù)挖掘?qū)W習(xí)。
三、使用 block 作為函數(shù)/方法多個(gè)返回值的方案
在 OC 中一個(gè)方法返回多個(gè)參數(shù)有一些方案,比如使用指針的指針(類似返回 NSError 的用法),或者聲明字典、模型或者自定義一個(gè) 元組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;
[self calculateA:a b:b result:^(Int sum, Int min, Int max) {
sumR = sum;
minR = min;
maxR = maxR;
}];
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;
resultBlock(a + b, MIN(a, b), MAX(a, b));
return [self doNothingButPrint];
}
存在使用了類的成員變量而導(dǎo)致循環(huán)引用的情況,需要使用 weak- strong-dance,并在訪問實(shí)例變量使用結(jié)構(gòu)體訪問成員的操作符 ->
@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
四、使用返回 對(duì)象本身的 block 實(shí)現(xiàn)鏈?zhǔn)秸Z法
經(jīng)典的例子是 OC 的自動(dòng)布局第三方框架 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);
}];
其內(nèi)部實(shí)現(xiàn)的主要思路是兩個(gè)
- 聲明一個(gè)只讀 block 屬性供外部調(diào)用,此 block 屬性返回實(shí)例本身
- 聲明一個(gè)屬性,調(diào)用后返回一個(gè)實(shí)例(可以是本身),并可以執(zhí)行額外操作
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
不難看出,在一定程度上說,方法都可以轉(zhuǎn)換成 block 的形式調(diào)用,而 block 連續(xù)返回實(shí)例本身,從而可以實(shí)現(xiàn)鏈?zhǔn)秸Z法調(diào)用,提高代碼的可讀性,嘗試實(shí)現(xiàn)一個(gè)鏈?zhǔn)郊臃ㄕ{(diào)用如下:
@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
/// 調(diào)用
Dot *dot = [[Dot alloc] init];
dot.add(1).then.add(2).then.add(3);
NSLog(@"now -> %zd", [dot getCurrent]); // 打印出 6
dot = nil;
使用鏈?zhǔn)秸Z法有一個(gè)很大的前提是調(diào)用 block 前該 block 不能為空,所以使用時(shí)注意挑選調(diào)用者不為空的場(chǎng)景,參考 Masonry。
再者,鏈?zhǔn)秸Z法在構(gòu)造構(gòu)造時(shí)有很好的優(yōu)勢(shì),比如純代碼構(gòu)造 UI 控件,推薦閱讀以下 LinkBlock, 從而這樣寫一個(gè) Label
UILabelNew
.labText(@"UILable").labNumberOfLines(0).labAlignment(NSTextAlignmentCenter)
.viewSetFrame(20,200,150,80)
.viewBGColor(@"#CCCCCC".strToUIColorFromHex())
.viewAddToView(self.view);
此外,block 在很多開源項(xiàng)目中都有很好的實(shí)踐,推薦:
誠邀你來討論我的專題, QQ群 287698622
加我微信溝通。
