不可变集合类型

下面的方法适用于如下类型:

NSArray
NSDictionary
NSIndexSet
NSSet
NSOrderedSet
- (void)bk_each:(void (^)(id obj))block;

对数组内的每个元素进行block指定的处理

- (void)bk_apply:(void (^)(id obj))block;

这个功能上和bk_each其实是一致的,但是它是异步的,速度上会比bk_each快,特别是在双核处理器上,但是必须注意线程安全,并且它不是按照顺序处理的。

- (id)bk_match:(BOOL (^)(id obj))block;

会找到第一个经过block处理后返回YES的元素

- (NSArray *)bk_select:(BOOL (^)(id obj))block;

对所有对元素进行block处理,将所有返回YES的元素放到一个数组中返回。

- (NSArray *)bk_reject:(BOOL (^)(id obj))block;

这个会剔除掉不匹配的

- (NSArray *)bk_map:(id (^)(id obj))block;

对数组进行通用block进行处理

- (id)bk_reduce:(id)initial withBlock:(id (^)(id sum, id obj))block;

- (NSInteger)bk_reduceInteger:(NSInteger)initial withBlock:(NSInteger(^)(NSInteger result, id obj))block;

- (CGFloat)bk_reduceFloat:(CGFloat)inital withBlock:(CGFloat(^)(CGFloat result, id obj))block;

对所有的元素进行递归处理,不断累加效果。

- (BOOL)bk_any:(BOOL (^)(id obj))block;

查看数组内部是否有满足条件的元素

- (BOOL)bk_none:(BOOL (^)(id obj))block;

查看数组内是否没有满足条件的元素

- (BOOL)bk_all:(BOOL (^)(id obj))block;

查看数组内是否全部原始都满足

- (BOOL)bk_corresponds:(NSArray *)list withBlock:(BOOL (^)(id obj1, id obj2))block;

查看当前数组和list的对应元素是否对应“相等”。

可变集合类型

下面的方法适用于如下几种可变集合类型:

NSMutableArray
NSMutableDictionary
NSMutableIndexSet
NSMutableOrderedSet
NSMutableSet
- (void)bk_performSelect:(BOOL (^)(id obj))block;

找出满足条件的

- (void)bk_performReject:(BOOL (^)(id obj))block;

剔除不满足条件的

- (void)bk_performMap:(id (^)(id obj))block;

对各个元素进行map处理

关联对象

- (void)bk_associateValue:(id)value withKey:(const void *)key;
+ (void)bk_associateValue:(id)value withKey:(const void *)key;
- (void)bk_atomicallyAssociateValue:(id)value withKey:(const void *)key;
+ (void)bk_atomicallyAssociateValue:(id)value withKey:(const void *)key;
- (void)bk_associateCopyOfValue:(id)value withKey:(const void *)key;
+ (void)bk_associateCopyOfValue:(id)value withKey:(const void *)key;
- (void)bk_atomicallyAssociateCopyOfValue:(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_weaklyAssociateValue:(__autoreleasing id)value withKey:(const void *)key;
- (id)bk_associatedValueForKey:(const void *)key;
+ (id)bk_associatedValueForKey:(const void *)key;
- (void)bk_removeAllAssociatedObjects;
+ (void)bk_removeAllAssociatedObjects;

NSInvocation

NSInvocation *invocation = [NSInvocation bk_invocationWithTarget:self block:^(id target) {
[target testMethod];
}];

[invocation invoke];

- (void)testMethod {
NSLog(@"===>");
}

这个用于触发某个target的对应方法,在block中传递进来target对象,直接使用该对象调用方法。

performSelector 的简化

它的最大优点在于能够取消已经对应的block。

- (id)bk_performBlock:(void (^)(id obj))block afterDelay:(NSTimeInterval)delay;
+ (id)bk_performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay;
- (id)bk_performBlockInBackground:(void (^)(id obj))block afterDelay:(NSTimeInterval)delay;
+ (id)bk_performBlockInBackground:(void (^)(void))block afterDelay:(NSTimeInterval)delay;
+ (id)bk_performBlock:(void (^)(void))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)
delay;
- (id)bk_performBlock:(void (^)(id obj))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay;
+ (void)bk_cancelBlock:(id)block;

KVO

- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath task:(void (^)(id target))task;
- (NSString *)bk_addObserverForKeyPaths:(NSArray *)keyPaths task:(void (^)(id obj, NSString *keyPath))task;
- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSDictionary *change))task;
- (NSString *)bk_addObserverForKeyPaths:(NSArray *)keyPaths options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task;
- (void)bk_addObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)token options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSDictionary *change))task;
- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths identifier:(NSString *)token options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task;
- (void)bk_removeObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)token;
- (void)bk_removeObserversWithIdentifier:(NSString *)token;
- (void)bk_removeAllBlockObservers;

NSTimer

+ (NSTimer *)bk_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))inBlock repeats:(BOOL)inRepeats;
+ (NSTimer *)bk_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))inBlock repeats:(BOOL)inRepeats;

UIView

- (void)bk_whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(void (^)(void))block;

numberOfTouches 多少个手指
numberOfTaps 点击多少次

规定多少个手指,多少点击多少次才会触发该handler Block进行处理。

- (void)bk_whenTapped:(void (^)(void))block;
- (void)bk_whenDoubleTapped:(void (^)(void))block;

单击双击UIView对象会执行指定的block

- (void)bk_eachSubview:(void (^)(UIView *subview))block;

非递归方式遍历当前View的子View

UIGestureRecognizer

UITapGestureRecognizer *singleTap = [UITapGestureRecognizer recognizerWithHandler:^(id sender) {
NSLog(@"Single tap.");
} delay:0.18];
[self addGestureRecognizer:singleTap];

UITapGestureRecognizer *doubleTap = [UITapGestureRecognizer recognizerWithHandler:^(id sender) {
[singleTap cancel];
NSLog(@"Double tap.");
}];
doubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:doubleTap];

UIControl

- (void)bk_addEventHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents;
- (void)bk_removeEventHandlersForControlEvents:(UIControlEvents)controlEvents;
- (BOOL)bk_hasEventHandlersForControlEvents:(UIControlEvents)controlEvents;

部分源码解析:

UIControl
- (void)bk_addEventHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents{

NSParameterAssert(handler);

//获取对象绑定到的事件
NSMutableDictionary *events = objc_getAssociatedObject(self, BKControlHandlersKey);
if (!events) {
events = [NSMutableDictionary dictionary];
//如果当前为首次给对象添加事件,则新建一个事件字典,并动态关联到对象
objc_setAssociatedObject(self, BKControlHandlersKey, events, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

//取出该事件的处理者
NSNumber *key = @(controlEvents);
NSMutableSet *handlers = events[key];
if (!handlers) {
handlers = [NSMutableSet set];
events[key] = handlers;
}
//将处理的Block 以及处理类型存储到BKControlWrapper对象
BKControlWrapper *target = [[BKControlWrapper alloc] initWithHandler:handler forControlEvents:controlEvents];
[handlers addObject:target];
//触发BKControlWrapper的invoke方法。
[self addTarget:target action:@selector(invoke:) forControlEvents:controlEvents];
}

UIControl+BlocksKit 中是通过为每个UIControl对象添加一个以UIControlEvents为key,value为BKControlWrapper集合的字典,当我们要为某个UIControlEvents添加一个事件的时候只要找到对应的handlers集合,然后将handler和UIControlEvents封装到BKControlWrapper,然后再调用[self addTarget:target action:@selector(invoke:) forControlEvents:controlEvents];
这样一旦发生controlEvents事件,就会触发BKControlWrapper的invoke方法,在invoke方法中就会执行对应的block。

UIGestureRecognizer
+ (id)bk_recognizerWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay {
return [[[self class] alloc] bk_initWithHandler:block delay:delay];
}

- (id)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay {
self = [self initWithTarget:self action:@selector(bk_handleAction:)];
if (!self) return nil;
self.bk_handler = block;
self.bk_handlerDelay = delay;
return self;
}

初始化的时候会将delay以及block保存到bk_handler以及bk_handlerDelay,并指定当前处理的Action为bk_handleAction

- (void)bk_handleAction:(UIGestureRecognizer *)recognizer{
void (^handler)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) = recognizer.bk_handler;
if (!handler) return;

NSTimeInterval delay = self.bk_handlerDelay;
CGPoint location = [self locationInView:self.view];
void (^block)(void) = ^{
if (!self.bk_shouldHandleAction) return;
handler(self, self.state, location);
};

self.bk_shouldHandleAction = YES;

if (!delay) {
block();
return;
}

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), block);
}

在对应的手势事件触发的时候会调用bk_handleAction 这时候取出bk_handler,然后根据是否需要延迟执行来执行对应的bk_handler

介绍了UIGestureRecognizer+BlocksKit这里顺带介绍下UIView+BlocksKit,它实际上也是在UIView的基础上添加手势识别器来实现的:

- (void)bk_whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(void (^)(void))block {
if (!block) return;

UITapGestureRecognizer *gesture = [UITapGestureRecognizer bk_recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
if (state == UIGestureRecognizerStateRecognized) block();
}];

gesture.numberOfTouchesRequired = numberOfTouches;
gesture.numberOfTapsRequired = numberOfTaps;

[self.gestureRecognizers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (![obj isKindOfClass:[UITapGestureRecognizer class]]) return;

UITapGestureRecognizer *tap = obj;
BOOL rightTouches = (tap.numberOfTouchesRequired == numberOfTouches);
BOOL rightTaps = (tap.numberOfTapsRequired == numberOfTaps);
if (rightTouches && rightTaps) {
[gesture requireGestureRecognizerToFail:tap];
}
}];

[self addGestureRecognizer:gesture];
}

- (void)bk_whenTapped:(void (^)(void))block{
[self bk_whenTouches:1 tapped:1 handler:block];
}
关联对象

这部分没太多新颖的内容,比较有意思的是实现weak方式的关联,它实际上使用了一个中间对象,来做过渡,它持有一个weak类型的属性value来存储实际所赋的值。在获取关联对象时就判断其是否为_BKWeakAssociatedObject类型的对象,如果是就返回该对象value的属性值。

+ (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;
}
NSObject+BKBlockExecution
- (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);
}

这里使用了一个__block的cacelled标记,标记无效时才执行block。默认情况下就传入NO,使其能正常工作。将要取消时传入YES即可,所以在取消的时候就直接调用了wrapper(YES);

动态代理

BlocksKit的动态代理部分是整个BlocksKit的精华所在,它用于解决什么问题呢?
我们先来看下我们要给一个对象设置代理一般有如下步骤:

1. 创建代理对象
2. 代理对象遵守协议;
3. 代理对象实现协议
4. 将代理对象赋给delegate属性

这有个不好的地方就是代码会显得比较散乱,必须要新建一个代理类,并且代理实现方法和设置代理的方法是分开的,这种问题之前我们会采用rac_signalForSelector来将代理实现聚合在设置代理的方法地方。但是这还不算是十分优雅的方式:

在BlocksKit中给出下面的一个例子:

UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:@"Hello World!"
message:@"This alert's delegate is implemented using blocks. That's so cool!"
delegate:nil
cancelButtonTitle:@"Meh."
otherButtonTitles:@"Woo!", nil];

// Get the dynamic delegate
A2DynamicDelegate *dd = alertView.bk_dynamicDelegate;

// Implement -alertViewShouldEnableFirstOtherButton:
[dd implementMethod:@selector(alertViewShouldEnableFirstOtherButton:) withBlock:^(UIAlertView *alertView) {
NSLog(@"Message: %@", alertView.message);
return YES;
}];

// Implement -alertView:willDismissWithButtonIndex:
[dd implementMethod:@selector(alertView:willDismissWithButtonIndex:) withBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
NSLog(@"You pushed button #%d (%@)", buttonIndex, [alertView buttonTitleAtIndex:buttonIndex]);
}];

// Set the delegate
alertView.delegate = dd;

[alertView show];

这个就是BlocksKit动态代理的使用过程:

首先通过bk_dynamicDelegate来获得UIAlertView的动态代理,然后通过implementMethod将代理方法selector与对应的block绑定,最后将动态代理设置给UIAlertView。
整个代码一气呵成。但是背后实际上还是存在代理类的创建,后面会一一向大家介绍整个流程,我们先来看下涉及到的关键类有哪些:

A2BlockInvocation 的主要用于存储和转发block
A2DynamicDelegate 用来实现类的代理和数据源
NSObject+A2DynamicDelegate 负责为返回 bk_dynamicDelegate bk_dynamicDataSource 等 A2DynamicDelegate 类型的实例,
NSObject+A2BlockDelegate 提供了一系列接口将代理方法映射到 block

我们接下来顺着上面的例子过一遍代码,看下如何实现动态代理的功能。

在载入UIAlertView+BlocksKit 分类的时候会调用load方法,这里主要做了两件事情,一个是通过bk_registerDynamicDelegate来注册动态代理,另一个是通过bk_linkDelegateMethods将指定的Block与对应的Delegate方法链接起来。

+ (void)load{
@autoreleasepool {
[self bk_registerDynamicDelegate];
[self bk_linkDelegateMethods:@{
@"bk_willShowBlock": @"willPresentAlertView:",
@"bk_didShowBlock": @"didPresentAlertView:",
@"bk_willDismissBlock": @"alertView:willDismissWithButtonIndex:",
@"bk_didDismissBlock": @"alertView:didDismissWithButtonIndex:",
@"bk_shouldEnableFirstOtherButtonBlock": @"alertViewShouldEnableFirstOtherButton:"
}];
}
}

注册动态代理

注册动态代理相关代码如下:

+ (void)bk_registerDynamicDelegate {
[self bk_registerDynamicDelegateNamed:@"delegate" forProtocol:a2_delegateProtocol(self/*UIAlertView*/)/*UIAlertViewDelegate*/];
}
Protocol *a2_delegateProtocol(Class cls) {
/*获取到UIActionSheetDelegate协议*/
return a2_classProtocol(cls, @"Delegate", @"delegate");
}

static Protocol *a2_classProtocol(Class _cls/*UIAlertView*/, NSString *suffix/*Delegate*/, NSString *description/*delegate*/) {
Class cls = _cls;
while (cls) {
NSString *className = NSStringFromClass(cls);
//UIAlertViewDelegate
NSString *protocolName = [className stringByAppendingString:suffix];
//获得UIAlertViewDelegate协议
Protocol *protocol = objc_getProtocol(protocolName.UTF8String);
if (protocol) return protocol;
cls = class_getSuperclass(cls);
}
//............
}
+ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName {
[self bk_registerDynamicDelegateNamed:delegateName forProtocol:a2_delegateProtocol(self)];
}
+ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName/*delegate*/ forProtocol:(Protocol *)protocol/*UIAlertViewDelegate*/ {
NSMapTable *propertyMap = [self bk_delegateInfoByProtocol:YES];
A2BlockDelegateInfo *infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
if (infoAsPtr != NULL) { return; }

const char *name = delegateName.UTF8String; /*delegate*/
objc_property_t property = class_getProperty(self, name); /*获得UIAlertView 的 delegate property属性*/
SEL setter = setterForProperty(property, name); /*setDelegate:*/
SEL a2_setter = prefixedSelector(setter); /*a2_SetDelegate:*/
SEL getter = getterForProperty(property, name); /*delegate*/

//A2BlockDelegateInfo 包着setter,a2_setter,getter 三个Selector
A2BlockDelegateInfo info = {
setter, a2_setter, getter
};

//这里将A2BlockDelegateInfo设置propertyMap到
[propertyMap setObject:(__bridge id)&info forKey:protocol];
infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];

//交换delegate setter的selector,也就是说在XXXX.delegate 的时候就会将delegte值赋给dynamicDelegate.realDelegate
IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id delegate) {
A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES);
//如果dynamicDelegate 等于 delegate的话也不使用delegate
if ([delegate isEqual:dynamicDelegate]) {
delegate = nil;
}
//dynamicDelegate 表示真实的delegate
dynamicDelegate.realDelegate = delegate;
});

//将设置方法setter设为a2_SetDelegate
if (!swizzleWithIMP(self, setter, a2_setter, setterImplementation, "v@:@", YES)) {
bzero(infoAsPtr, sizeof(A2BlockDelegateInfo));
return;
}

if (![self instancesRespondToSelector:getter]) {
IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
//获取到对应的动态代理对象
return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)];
});
addMethodWithIMP(self, getter, NULL, getterImplementation, "@@:", NO);
}
}
static inline A2DynamicDelegate *getDynamicDelegate(NSObject *delegatingObject,/*被代理对象*/ Protocol *protocol, const A2BlockDelegateInfo *info, BOOL ensuring) {
A2DynamicDelegate *dynamicDelegate = [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)/*UIAlertViewDelegate*/];
//......
return dynamicDelegate;
}
- (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol{
Class class = [A2DynamicDelegate class];
/*UIAlertViewDelegate*/
NSString *protocolName = NSStringFromProtocol(protocol);
if ([protocolName hasSuffix:@"Delegate"]) {
class = a2_dynamicDelegateClass([self class], @"Delegate");
} else if ([protocolName hasSuffix:@"DataSource"]) {
class = a2_dynamicDelegateClass([self class], @"DataSource");
}
/*A2DynamicUIAlertViewDelegate*/
return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
}
static Class a2_dynamicDelegateClass(Class cls, NSString *suffix) {
while (cls) {
//A2DynamicUIAlertViewDelegate
NSString *className = [NSString stringWithFormat:@"A2Dynamic%@%@", NSStringFromClass(cls), suffix];
Class ddClass = NSClassFromString(className);
if (ddClass) return ddClass;
cls = class_getSuperclass(cls);
}

return [A2DynamicDelegate class];
}
- (id)bk_dynamicDelegateWithClass:(Class)cls forProtocol:(Protocol *)protocol{

__block A2DynamicDelegate *dynamicDelegate;

dispatch_sync(a2_backgroundQueue(), ^{
dynamicDelegate = objc_getAssociatedObject(self, (__bridge const void *)protocol);

if (!dynamicDelegate) {
//将当前代理与当前对象关联,这里可以起到缓存作用
dynamicDelegate = [[cls alloc] initWithProtocol:protocol];
objc_setAssociatedObject(self, (__bridge const void *)protocol, dynamicDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
});
return dynamicDelegate;
}

最为关键的在于:

+ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName forProtocol:(Protocol *)protocol;

以及

- (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol

在bk_registerDynamicDelegateNamed方法中会将通过bk_dynamicDelegateForProtocol方法获取到的代理对象通过关联对象与当前对象产生关联,也就是说将A2DynamicUIAlertViewDelegate对象作为当前的dynamicDelegate属性添加到当前类中,并且原本的delegate存到dynamicDelegate 的 realDelegate对象中,获取delegate的时候,返回就是dynamicDelegate这个管理对象。从而达到替换的目的。也就是说在我们调用delegate的时候实际上是返回A2DynamicUIAlertViewDelegate,这时候系统调用delegate方法的时候会转调A2DynamicUIAlertViewDelegate对应的方法,

我们接下来看下A2DynamicUIAlertViewDelegate中的具体一个代理方法:

- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView {
BOOL should = YES;
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(alertViewShouldEnableFirstOtherButton:)])
should &= [realDelegate alertViewShouldEnableFirstOtherButton:alertView];

BOOL (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd];
if (block)
should &= block(alertView);

return should;
}

在这里调用了blockImplementationForMethod,通过这个方法取出这个代理对应的Block执行。我们看下blockImplementationForMethod方法:

- (id)blockImplementationForMethod:(SEL)selector{
A2BlockInvocation *invocation = nil;
if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector]))
return invocation.block;
return NULL;
}

这个方法会通过selector 从 A2BlockInvocation中获取对应的invocation,而block就存在这里,这个接下来会进行介绍。

这里做个简单的总结:

首先在load方法中通过bk_registerDynamicDelegate方法将delegate替换为我们已经实现好的A2DynamicUIAlertViewDelegate,这样在系统事件发生需要调用delegate方法的时候就会将流程转到A2DynamicUIAlertViewDelegate,在这里会通过当前selector从A2BlockInvocation中获取到对应的invocation,block就存在这个invocation中。那么这里就会产生一个问题,selector怎么和invocation产生关联的?我们接下来介绍这部分。

如果我们还有印象的话在load方法中还调用了bk_linkDelegateMethods

+ (void)load{
@autoreleasepool {
[self bk_registerDynamicDelegate];
[self bk_linkDelegateMethods:@{
@"bk_willShowBlock": @"willPresentAlertView:",
@"bk_didShowBlock": @"didPresentAlertView:",
@"bk_willDismissBlock": @"alertView:willDismissWithButtonIndex:",
@"bk_didDismissBlock": @"alertView:didDismissWithButtonIndex:",
@"bk_shouldEnableFirstOtherButtonBlock": @"alertViewShouldEnableFirstOtherButton:"
}];
}
}
+ (void)bk_linkDelegateMethods:(NSDictionary *)dictionary{
[self bk_linkProtocol:a2_delegateProtocol(self) methods:dictionary];
}

bk_linkProtocol是这部分最为关键的方法,会将block与delegate中的selector进行关联。

+ (void)bk_linkProtocol:(Protocol *)protocol methods:(NSDictionary *)dictionary {

[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *selectorName, BOOL *stop) {

//......
IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject, protocol, info, NO);
return [delegate blockImplementationForMethod:selector];
});

if (!class_addMethod(self, getter, getterImplementation, "@@:")) {
NSCAssert(NO, @"Could not implement getter for \"%@\" property.", propertyName);
}

IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id block) {
A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject, protocol, info, YES);
[delegate implementMethod:selector withBlock:block];
});

if (!class_addMethod(self, setter, setterImplementation, "v@:@")) {
NSCAssert(NO, @"Could not implement setter for \"%@\" property.", propertyName);
}
}];
}

我们看下implementMethod

- (void)implementMethod:(SEL)selector withBlock:(id)block {
BOOL isClassMethod = self.isClassProxy;
//.......
struct objc_method_description methodDescription = protocol_getMethodDescription(self.protocol, selector, YES, !isClassMethod);
if (!methodDescription.name) methodDescription = protocol_getMethodDescription(self.protocol, selector, NO, !isClassMethod);

A2BlockInvocation *inv = nil;
if (methodDescription.name) {
NSMethodSignature *protoSig = [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
inv = [[A2BlockInvocation alloc] initWithBlock:block methodSignature:protoSig];
} else {
inv = [[A2BlockInvocation alloc] initWithBlock:block];
}
[self.invocationsBySelectors bk_setObject:inv forSelector:selector];
}

在这里会新建一个A2BlockInvocation并将block 保存到 A2BlockInvocation中,并以selector为key A2BlockInvocation为value 存到self.invocationsBySelectors中。这样上一步就可以通过selector找到A2BlockInvocation进而找到关联的block了,这部分逻辑在blockImplementationForMethod中。

- (id)blockImplementationForMethod:(SEL)selector {
A2BlockInvocation *invocation = nil;
if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector]))
return invocation.block;
return NULL;
}

但是你们想过没为什么要通过A2BlockInvocation而不是直接将block与selector关联?这其实涉及到下面这些部分,往分类添加的属性,这些属性分类中并没有设置它的getter方法和setter方法。

@property (nonatomic, copy, setter = bk_setCancelBlock:) void (^bk_cancelBlock)(void);

@property (nonatomic, copy, setter = bk_setWillShowBlock:) void (^bk_willShowBlock)(UIAlertView *alertView);

@property (nonatomic, copy, setter = bk_setDidShowBlock:) void (^bk_didShowBlock)(UIAlertView *alertView);

@property (nonatomic, copy, setter = bk_setWillDismissBlock:) void (^bk_willDismissBlock)(UIAlertView *alertView, NSInteger buttonIndex);

@property (nonatomic, copy, setter = bk_setDidDismissBlock:) void (^bk_didDismissBlock)(UIAlertView *alertView, NSInteger buttonIndex);

@property (nonatomic, copy, setter = bk_SetShouldEnableFirstOtherButtonBlock:) BOOL (^bk_shouldEnableFirstOtherButtonBlock)(UIAlertView *alertView) NS_AVAILABLE_IOS(5_0);

这样做的目的就是为了通过消息分发机制来找到对应的block。

@implementation UIAlertView (BlocksKit)
@dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock, bk_shouldEnableFirstOtherButtonBlock;

整个分发过程在A2BlockInvocation类中。

到此为止整个流程已经结束,相关细节可以查看代码中的具体实现。

Contents
  1. 1. 不可变集合类型
  2. 2. 可变集合类型
  3. 3. 关联对象
  4. 4. NSInvocation
  5. 5. performSelector 的简化
  6. 6. KVO
  7. 7. NSTimer
  8. 8. UIView
  9. 9. UIGestureRecognizer
  10. 10. UIControl
  11. 11. 部分源码解析:
    1. 11.1. UIControl
    2. 11.2. UIGestureRecognizer
    3. 11.3. 关联对象
    4. 11.4. NSObject+BKBlockExecution
    5. 11.5. 动态代理