1引言 众所周知Block已被广泛用于iOS编程。它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调。Block比传统回调函数有2点优势:
允许在调用点上下文书写执行逻辑,不用分离函数 Block可以使用local variables.  
基于以上种种优点Cocoa Touch越发支持Block式编程,这点从UIView的各种动画效果可用Block实现就可见一斑。而BlocksKit是对Cocoa Touch Block编程更进一步的支持,它简化了Block编程,发挥Block的相关优势,让更多UIKit类支持Block式编程。
2BlocksKit目录结构 BlocksKit代码存放在4个目录中分别是Core、DynamicDelegate、MessageUI、UIKit。其中:
Core  存放Foundation Kit相关的Block categoryDynamicDelegate  动态代理(一种事件转发机制)相关代码MessageUI  存放MessageUI相关的Block categoryUIKit  存放UIKit相关的Block category 
本文尝试去梳理BlocksKit的实现方式并列举BlocksKit使用的例子。
3Core相关代码分析和举例 Core文件夹下面的代码可以分为如下几个部分:
容器相关 (NSArray、NSDictionary、NSSet、NSIndexSet、NSMutableArray、NSMutableDictionary、NSMutableSet、NSMutableIndexSet)关联对象相关 逻辑执行相关 KVO相关 定时器相关  
3.1容器相关的BlocksKit 不管是可变容器还是不可变容器,容器相关的BlocksKit代码总体上说是对容器原生block相关函数的封装。容器相关的BlocksKit函数更加接近自然语义,有一种函数式编程和语义编程的感觉。
以下给出部分不可变容器的BlocksKit声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - (void )bk_each:(void  (^)(id  obj))block;  - (void )bk_apply:(void  (^)(id  obj))block; - (id )bk_match:(BOOL  (^)(id  obj))block; - (NSArray  *)bk_select:(BOOL  (^)(id  obj))block; - (NSArray  *)bk_reject:(BOOL  (^)(id  obj))block; - (NSArray  *)bk_map:(id  (^)(id  obj))block; - (BOOL )bk_any:(BOOL  (^)(id  obj))block;  - (BOOL )bk_none:(BOOL  (^)(id  obj))block; - (BOOL )bk_all:(BOOL  (^)(id  obj))block; 
其中bk_all函数的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (BOOL )bk_all:(BOOL  (^)(id  obj))block {     NSParameterAssert (block != nil );     __block BOOL  result = YES ;     [self  enumerateObjectsUsingBlock:^(id  obj, NSUInteger  idx, BOOL  *stop) {         if  (!block(obj)) {             result = NO ;             *stop = YES ;         }     }];     return  result; } 
从以上各种函数声明以及bk_all函数的定义我们就可以看出,容器相关的BlocksKit函数确实更加接近自然语义,简化编程。
再给出部分不可变容器的BlocksKit声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 - (void )bk_performSelect:(BOOL  (^)(id  obj))block; - (void )bk_performReject:(BOOL  (^)(id  obj))block; - (void )bk_performMap:(id  (^)(id  obj))block; 
3.2关联对象相关的BlocksKit 关联对象的作用如下:关联对象的例子  
在我们的实际项目中的常见用法一般有category中用关联对象定义property,或者使用关联对象绑定一个block。
关联对象相关的BlocksKit是对objc_setAssociatedObject、objc_getAssociatedObject、objc_removeAssociatedObjects这几个原生关联对象函数的封装。主要是封装其其内存管理语义。
部分函数声明如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 - (void )bk_associateValue:(id )value withKey:(const  void  *)key; - (void )bk_associateCopyOfValue:(id )value withKey:(const  void  *)key; - (void )bk_atomicallyAssociateValue:(id )value withKey:(const  void  *)key; - (void )bk_atomicallyAssociateCopyOfValue:(id )value withKey:(const  void  *)key; - (void )bk_weaklyAssociateValue:(__autoreleasing id )value withKey:(const  void  *)key; - (void )bk_removeAllAssociatedObjects; 
除了弱绑定 以外,其它BlocksKit函数都是简单封装。只有弱绑定看起来挺牛B,有点扩展关联对象原生语义的感觉。
弱绑定实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @interface  _BKWeakAssociatedObject  : NSObject @property  (nonatomic , weak ) id  value;@end - (void )bk_weaklyAssociateValue:(__autoreleasing id )value withKey:(const  void  *)key {     _BKWeakAssociatedObject *assoc = objc_getAssociatedObject(self , key);     if  (!assoc) {         assoc = [_BKWeakAssociatedObject new];                                    [self  bk_associateValue:assoc withKey:key];     }          assoc.value = value; } 
从以上实现代码可以看出,所谓弱绑定实际上是在关联对象之间做了一个中间层,让本对象以OBJC_ASSOCIATION_RETAIN_NONATOMIC的形式去关联中间层(_BKWeakAssociatedObject),而中间层又以weak property的形式去存储真正关联对象的指针。
3.3逻辑执行相关的BlocksKit 所谓逻辑执行,就是Block块执行。逻辑执行相关的BlocksKit是对dispatch_after函数的封装。使其更加符合语义。
主要函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 - (id )bk_performBlock:(void  (^)(id  obj))block afterDelay:(NSTimeInterval )delay - (id )bk_performBlockInBackground:(void  (^)(id  obj))block afterDelay:(NSTimeInterval )delay - (id )bk_performBlock:(void  (^)(id  obj))block onQueue:(dispatch_queue_t )queue afterDelay:(NSTimeInterval )delay; + (void )bk_cancelBlock:(id )block; 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 - (id )bk_performBlock:(void  (^)(id  obj))block onQueue:(dispatch_queue_t )queue afterDelay:(NSTimeInterval )delay {     NSParameterAssert (block != nil );          __block BOOL  cancelled = NO ;                    void  (^wrapper)(BOOL ) = ^(BOOL  cancel) {              if  (cancel) {             cancelled = YES ;             return ;         }         if  (!cancelled) block(self );     };               dispatch_after(BKTimeDelay(delay), queue, ^{                  wrapper(NO );     });               return  [wrapper copy ]; } + (void )bk_cancelBlock:(id )block {     NSParameterAssert (block != nil );     void  (^wrapper)(BOOL ) = block;          wrapper(YES ); } 
以上代码给出了bk_performBlock和bk_cancelBlock的实现。其中bk_performBlock对参数block进行一层封装后再用dispatch_after投入相应的queue。注意该函数会返回的wrapper block,以供cancel。
3.4KVO相关BlocksKit KVO主要涉及两类对象,即“被观察对象“和“观察者“。
与“被观察对象”相关的函数主要有如下两个:
1 2 3 4 - (void )addObserver:(NSObject  *)observer forKeyPath:(NSString  *)keyPath options:(NSKeyValueObservingOptions )options context:(void  *)context; - (void )removeObserver:(NSObject  *)observer forKeyPath:(NSString  *)keyPath context:(void  *)context; 
与“观察者“相关的函数如下:
1 2 - (void )observeValueForKeyPath:(NSString  *)keyPath ofObject:(id )object change:(NSDictionary  *)change context:(void  *)context; 
通常的KVO做法是先对“被观察对象”添加“观察者”,同时在“观察者”中实现观察回调。这样每当“被观察对象”的指定property改变时,“观察者”就会调用观察回调。
KVO相关BlocksKit弱化了“观察者”这种对象,使得每当“被观察对象”的指定property改变时,就会调起一个block。具体实现方式是定义一个_BKObserver类,让该类实现观察回调、对被观察对象添加观察者和删除观察者。
_BKObserver类定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 @interface  _BKObserver  : NSObject      BOOL  _isObserving; } @property  (nonatomic , readonly , unsafe_unretained ) id  observee;@property  (nonatomic , readonly ) NSMutableArray  *keyPaths;@property  (nonatomic , readonly ) id  task;@property  (nonatomic , readonly ) BKObserverContext context;- (id )initWithObservee:(id )observee keyPaths:(NSArray  *)keyPaths context:(BKObserverContext)context task:(id )task; @end static  void  *BKObserverBlocksKey = &BKObserverBlocksKey;static  void  *BKBlockObservationContext = &BKBlockObservationContext;@implementation  _BKObserver - (id )initWithObservee:(id )observee keyPaths:(NSArray  *)keyPaths context:(BKObserverContext)context task:(id )task {     if  ((self  = [super  init])) {         _observee = observee;         _keyPaths = [keyPaths mutableCopy];         _context = context;         _task = [task copy ];     }     return  self ; } - (void )observeValueForKeyPath:(NSString  *)keyPath ofObject:(id )object change:(NSDictionary  *)change context:(void  *)context {          if  (context != BKBlockObservationContext) return ;     @synchronized (self ) {         switch  (self .context) {             case  BKObserverContextKey: {                 void  (^task)(id ) = self .task;                 task(object);                 break ;             }             case  BKObserverContextKeyWithChange: {                 void  (^task)(id , NSDictionary  *) = self .task;                 task(object, change);                 break ;             }             case  BKObserverContextManyKeys: {                 void  (^task)(id , NSString  *) = self .task;                 task(object, keyPath);                 break ;             }             case  BKObserverContextManyKeysWithChange: {                 void  (^task)(id , NSString  *, NSDictionary  *) = self .task;                 task(object, keyPath, change);                 break ;             }         }     } } - (void )startObservingWithOptions:(NSKeyValueObservingOptions )options {     @synchronized (self ) {         if  (_isObserving) return ;         [self .keyPaths bk_each:^(NSString  *keyPath) {                      [self .observee addObserver:self  forKeyPath:keyPath options:options context:BKBlockObservationContext];         }];         _isObserving = YES ;     } } - (void )stopObservingKeyPath:(NSString  *)keyPath {     NSParameterAssert (keyPath);     @synchronized  (self ) {         if  (!_isObserving) return ;         if  (![self .keyPaths containsObject:keyPath]) return ;         NSObject  *observee = self .observee;         if  (!observee) return ;         [self .keyPaths removeObject: keyPath];         keyPath = [keyPath copy ];         if  (!self .keyPaths.count) {             _task = nil ;             _observee = nil ;             _keyPaths = nil ;         }         [observee removeObserver:self  forKeyPath:keyPath context:BKBlockObservationContext];     } } 
具体添加删除KVO block的函数实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 - (void )bk_addObserverForKeyPaths:(NSArray  *)keyPaths identifier:(NSString  *)identifier options:(NSKeyValueObservingOptions )options context:(BKObserverContext)context task:(id )task {     NSParameterAssert (keyPaths.count);     NSParameterAssert (identifier.length);     NSParameterAssert (task);     Class classToSwizzle = self .class;     NSMutableSet  *classes = self .class.bk_observedClassesHash;     @synchronized  (classes) {         NSString  *className = NSStringFromClass (classToSwizzle);         if  (![classes containsObject:className]) {             SEL deallocSelector = sel_registerName("dealloc" );                          __block void  (*originalDealloc)(__unsafe_unretained  id , SEL) = NULL ;                                       id  newDealloc = ^(__unsafe_unretained  id  objSelf) {                 [objSelf bk_removeAllBlockObservers];                                  if  (originalDealloc == NULL ) {                     struct  objc_super superInfo = {                         .receiver = objSelf,                         .super_class = class_getSuperclass(classToSwizzle)                     };                                          void  (*msgSend)(struct  objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;                     msgSend(&superInfo, deallocSelector);                 } else  {                     originalDealloc(objSelf, deallocSelector);                 }             };                          IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);                          if  (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:" )) {                                  Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);                                                                    originalDealloc = (void (*)(__unsafe_unretained  id , SEL))method_getImplementation(deallocMethod);                                                   originalDealloc = (void (*)(__unsafe_unretained  id , SEL))method_setImplementation(deallocMethod, newDeallocIMP);             }                          [classes addObject:className];         }     }         NSMutableDictionary  *dict;     _BKObserver *observer = [[_BKObserver alloc] initWithObservee:self  keyPaths:keyPaths context:context task:task];          [observer startObservingWithOptions:options];         @synchronized  (self ) {         dict = [self  bk_observerBlocks];         if  (dict == nil ) {             dict = [NSMutableDictionary  dictionary];             [self  bk_setObserverBlocks:dict];         }     }     dict[identifier] = observer; } - (void )bk_removeAllBlockObservers {     NSDictionary  *dict;     @synchronized  (self ) {         dict = [[self  bk_observerBlocks] copy ];         [self  bk_setObserverBlocks:nil ];     }     [dict.allValues bk_each:^(_BKObserver *trampoline) {         [trampoline stopObserving];     }]; } 
3.5定时器相关BlocksKit NSTimer有个比较恶心的特性,它会持有它的target。比如在一个controller中使用了timer,并且timer的target设置为该controller本身,那么想在controller的dealloc中fire掉timer是做不到的,必须要在其他的地方fire。这会让编码很难受。具体参考《Effective Objective C》的最后一条。 BlocksKit解除这种恶心,其方式是把timer的target设置为timer 的class对象。把要执行的block保存在timer的userInfo中执行。因为timer 的class对象一直存在,所以是否被持有其实无所谓。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + (id )bk_scheduledTimerWithTimeInterval:(NSTimeInterval )inTimeInterval block:(void  (^)(NSTimer  *timer))block repeats:(BOOL )inRepeats {     NSParameterAssert (block != nil );     return  [self  scheduledTimerWithTimeInterval:inTimeInterval target:self  selector:@selector (bk_executeBlockFromTimer:) userInfo:[block copy ] repeats:inRepeats]; } + (id )bk_timerWithTimeInterval:(NSTimeInterval )inTimeInterval block:(void  (^)(NSTimer  *timer))block repeats:(BOOL )inRepeats {     NSParameterAssert (block != nil );     return  [self  timerWithTimeInterval:inTimeInterval target:self  selector:@selector (bk_executeBlockFromTimer:) userInfo:[block copy ] repeats:inRepeats]; } + (void )bk_executeBlockFromTimer:(NSTimer  *)aTimer {     void  (^block)(NSTimer  *) = [aTimer userInfo];     if  (block) block(aTimer); } 
4BlocksKit源码分析(一)小结 本文介绍了BlocksKit源码的组织结构:Core、DynamicDelegate、MessageUI、UIKit。并简要分析了Core的源码。该部分代码由5个小部分组成:容器、关联对象、逻辑执行、KVO、定时器。由源码可以看出BlocksKit扩展更加符合自然语义,如容器相关代码;BlocksKit一般通过中间层来承载回调函数,并在中间层回调的时候调用相应block,如KVO的BlocksKit实现。总体来说,Core代码是这些代码的最基础部分,具有很强的通用性。