神奇的 BlocksKit (二)


Blog: [Draveness](http://draveness.me)


这篇文章『神奇的 BlocksKit』的第二部分,关于第一部分的内容在这里:


+ [神奇的 BlocksKit(一)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/BlocksKit/神奇的%20BlocksKit%20(一).md)

+ [神奇的 BlocksKit(二)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/BlocksKit/神奇的%20BlocksKit%20(二).md)



## 动态代理


动态代理这部分可以说是 BlocksKit 的精华。它**使用 block 属性替换 UIKit 中的所有能够通过代理完成的事件**,省略了设置代理和实现方法的过程,让对象自己实现代理方法,而且这个功能的实现是极其动态的。


> 其实不是对象自己实现的代理方法,只是框架为我们提供的便捷方法,不需要构造其它对象就能完成代理方法的实现,具体我们会在后面详细地解释。


下面是这部分几个关键的类:


+ `A2BlockInvocation` 的主要作用是存储和转发 block

+ `A2DynamicDelegate` 用来实现类的代理和数据源,它是 `NSProxy` 的子类

+ `NSObject+A2DynamicDelegate` 负责为返回 `bk_dynamicDelegate` `bk_dynamicDataSource` `A2DynamicDelegate` 类型的实例,为 `NSObject` 提供主要的接口

+ `NSObject+A2BlockDelegate` 提供了一系列接口将代理方法映射到 block

+ 其他的 UIKit 的分类提供对应的属性,并在对应的 `A2DynamicDelegate` 子类中实现代理方法


这里是我对这部分代码结构的理解:


![](../images/blockskit.png)


这篇文成首先会从上到下对整个工作原理进行概述,然后再从底层到顶层详细地解释这个框架的机制和原理。


### 动态代理工作概述


在这里我们要对这部分的实现进行一个简单的概述,从上到下跟踪 BlocksKit 的调用过程。


`UIImagePickerController` 为例,因为这个文件中的代码较少,能省去很多不必要的实现细节。


在头文件中声明了两个属性,也就是 `UIImagePickerController` 代理方法的对应 block 属性:


```objectivec

@property (nonatomiccopy) void(^bk_didFinishPickingMediaBlock)(UIImagePickerController *NSDictionary *);

@property (nonatomiccopy) void(^bk_didCancelBlock)(UIImagePickerController *);

```


然后在实现文件中动态生成这两个方法的存取方法


```objectivec

@dynamic bk_didFinishPickingMediaBlock;

@dynamic bk_didCancelBlock;

```


你可以看到在这个名为 `BlocksKit` 的分类中只添加了一个方法:


```objectivec

+ (void)load

{

@autoreleasepool {

[self bk_registerDynamicDelegate];

[self bk_linkDelegateMethods:@{ @"bk_didFinishPickingMediaBlock": @"imagePickerController:didFinishPickingMediaWithInfo:",

                                        @"bk_didCancelBlock": @"imagePickerControllerDidCancel:" }];

}

}

```


`load` 中实现这个方法,能够减少其中两个方法的调用次数,在 `autoreleasepool` 块中调用方法,使得其它地方的代码不会受到这里注册代理,链接代理方法中产生的对象的影响。


`bk_registerDynamicDelegate` 方法是 `NSObject+A2BlockDelegate` 分类中添加的方法,用于修改原有属性 `delegate` 方法的实现(**动态替换 delegate 方法实现**)。在这里就是与 `UIImagePickerController+BlocksKit` 处于同一文件下的 `A2DynamicUIImagePickerControllerDelegate`,先不说这个文件的功能,会在之后介绍。


`NSObject+A2DynamicDelegate` 分类中的 `bk_registerDynamicDelegateNamed:forProtocol:` 修改 `@selector(delegate)` `@selector(setDelegate:)` 的实现,使用 `A2DynamicUIImagePickerControllerDelegate` 替换原有的 `delegate`


```objectivec

IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObjectid delegate) {

A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObjectprotocolinfoAsPtrYES);

if ([delegate isEqual:dynamicDelegate]) {

delegate = nil;

}

dynamicDelegate.realDelegate = delegate;

});


IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {

    return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObjectprotocol)];

});

```


在获取 `delegate` 属性时,就会获取 `A2DynamicUIImagePickerControllerDelegate` `realDelegate` 相当于原有的 `delegate` 属性,会在下面的小节中具体分析。


`load` 方法中调用下一个方法是 `bk_linkDelegateMethods:` 这个方法会把代理方法和对应的 block 属性链接起来,这样可以通过代理方法的选择子查找对应的 block


```objectivec

IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {

A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObjectprotocolinfoNO);

return [delegate blockImplementationForMethod:selector];

});


IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObjectid block) {

A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObjectprotocolinfoYES);

[delegate implementMethod:selector withBlock:block];

});

```


通过调用 `A2DynamicDelegate` 的实例方法 `blockImplementationForMethod:` `implementMethod:withBlock:` 动态实现 block 的存取方法。


当代理方法 `imagePickerController:didFinishPickingMediaWithInfo:` 被调用时,因为 `A2DynamicUIImagePickerControllerDelegate` `UIImagePickerController` 的代理,所以会调用它的方法:


```objectivec

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

id realDelegate = self.realDelegate;

if (realDelegate && [realDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingMediaWithInfo:)])

[realDelegate imagePickerController:picker didFinishPickingMediaWithInfo:info];


void (^block)(UIImagePickerController *NSDictionary *) = [self blockImplementationForMethod:_cmd];

if (block) block(pickerinfo);

}

```


通过 `blockImplementationForMethod:` 方法获取在上面存储的 block,然后传入参数执行该代码块。


1. `load` 方法注册动态代理并链接代理方法

2. 在运行时修改原有的 `delegate` 属性的存取方法,使用 `A2DynamicDelegate` 替换原有的 `delegate`,原有的 `delegate` 换为 `realDelegate`

3. block 属性动态实现存取方法,返回对应 `A2DynamicDelegate` 子类中存储的 block

4. 在代理方法真正被调用时,查找 `realDelegate` 中是否对代理方法做出响应,无论是否响应,都通过选择子查找对应的 `block`,然后传入相应参数执行 block


### 自底向上分析动态代理的工作


我们已经自顶向下分析了 BlocksKit 的工作过程,也对这个部分有一个基本的了解,接下来我们将从底层到顶层分析整个 BlocksKit,我们再来看一下整个框架的结构图:


![](../images/blockskit.png)


我们将以下面的顺序来依次介绍这些模块,其中的 `UITextField` 可以换成其它的类:


1. A2BlockInvocation

2. A2DynamicDelegate

3. NSObject+A2DynamicDelegate

4. A2DynamicUITextFieldDelegate

5. UITextField+BlocksKit


#### A2BlockInvocation


`A2BlockInvocation` 使用来对闭包,也就是 block 进行存储和转发的类。


先介绍这个的是因为 `A2BlockInvocation` 的功能比较底层,涉及的内容也都比较奇葩,所以想先简单介绍一下,避免之后一个类分几部分介绍。


Objective-C 中,每一个方法甚至 block 都是有类型签名的:


```objectivec

@interface NSMethodSignature : NSObject {


...


@property (readonly) NSUInteger numberOfArguments;


...


@property (readonly) const char *methodReturnType NS_RETURNS_INNER_POINTER;


...


@end

```


它们有返回类型、参数数字和参数类型等等。


##### Block 结构体


block 的签名没有哪个函数能够直接获取,它存储在 block 的结构体中,就像这样:


```objectivec

typedef NS_OPTIONS(intBKBlockFlags) {

BKBlockFlagsHasCopyDisposeHelpers = (1 << 25),

BKBlockFlagsHasSignature          = (1 << 30)

};


typedef struct _BKBlock {

__unused Class isa;

BKBlockFlags flags;

__unused int reserved;

void (__unused *invoke)(struct _BKBlock *block...);

struct {

unsigned long int reserved;

unsigned long int size;

// requires BKBlockFlagsHasCopyDisposeHelpers

void (*copy)(void *dstconst void *src);

void (*dispose)(const void *);

// requires BKBlockFlagsHasSignature

const char *signature;

const char *layout;

} *descriptor;

// imported variables

} *BKBlockRef;

```


这部分其实就是 block 实际存储在内存中的数据接口,可以在 runtime 中的源代码中看到这里的代码。


##### typeSignatureForBlock


上面的 `signature` 就是 block 的签名,下面实现方法来获取这个签名


```objectivec

+ (NSMethodSignature *)typeSignatureForBlock:(id)block __attribute__((purenonnull(1)))

{

BKBlockRef layout = (__bridge void *)block;


// 如果 block 没有签名直接返回空

if (!(layout->flags & BKBlockFlagsHasSignature))

return nil;


void *desc = layout->descriptor;

desc += 2 * sizeof(unsigned long int);


if (layout->flags & BKBlockFlagsHasCopyDisposeHelpers)

desc += 2 * sizeof(void *);


if (!desc)

return nil;


const char *signature = (*(const char **)desc);


return [NSMethodSignature signatureWithObjCTypes:signature];

}

```


知道了这个方法的作用再理解它的实现就非常简单了,根据`flag` 来移动指针,最终 `signature` 所在的内存空间。


>  Unlike a typical method signaturea block type signature has no `self` (`'@'`)

 or `_cmd` (`':'`) parameterbut instead just one parameter for the block itself

 (`'@?'`)


在这里所涉及的 `@``:` `@?` 可以看这里的文档 [类型编码](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)


在一般的方法签名中 block 的类型签名是没有 `self` (`'@'`) 或者 `_cmd` (`':'`) 的,只有一个参数代表 block 自己 (`'@?'`).


+ `^(UIActionSheet *) {}`

+ 参数类型:`@?(@"UIActionSheet")`

+ 返回类型:`v`

+ `- (void)willPresentActionSheet:(UIActionSheet *)actionSheet`

+ 参数类型:`@:@`

+ 返回类型:`v`


为什么要把 `@"UIActionSheet"` 标记上括号?因为它们属于同一个参数。


同时因为 `UIActionSheet` 也是 `id` 类型,所以它的类型编码也是 `@`


当调用 `initWithBlock:` 方法时,会先调用上面说的方法 `typeSignatureForBlock:` 获取 block 的类型签名:


```objectivec

- (instancetype)initWithBlock:(id)block

{

NSParameterAssert(block);

NSMethodSignature *blockSignature = [[self class] typeSignatureForBlock:block];

NSMethodSignature *methodSignature = [[self class] methodSignatureForBlockSignature:blockSignature];

NSAssert(methodSignature@"Incompatible block: %@"block);

return (self = [self initWithBlock:block methodSignature:methodSignature blockSignature:blockSignature]);

}

```


##### methodSignatureForBlockSignature


然后调用 `methodSignatureForBlockSignature:` 方法构造一个可以兼容的方法签名:


```objectivec

+ (NSMethodSignature *)methodSignatureForBlockSignature:(NSMethodSignature *)original

{

#1: 检查方法签名的参数,省略


NSMutableString *signature = [[NSMutableString alloc] initWithCapacity:original.numberOfArguments + 1];


const char *retTypeStr = original.methodReturnType;

// 返回类型,id 类型(self @),选择子类型(SEL :)

[signature appendFormat:@"%s%s%s"retTypeStr@encode(id)@encode(SEL)];

// signature = (返回类型)@:

for (NSUInteger i = 1; i < original.numberOfArguments; i++) {

const char *typeStr = [original getArgumentTypeAtIndex:i];

NSString *type = [[NSString alloc] initWithBytesNoCopy:(void *)typeStr length:strlen(typeStr) encoding:NSUTF8StringEncoding freeWhenDone:NO];

[signature appendString:type];

}

// signature = (返回类型)@:(参数类型)


return [NSMethodSignature signatureWithObjCTypes:signature.UTF8String];

}

```


具体的实现细节我们就省略了,它的工作原理是把 `@?(@"UIActionSheet")` 类型签名转换成 `@:@`,然后返回方法签名。


关于代码中的 `@encode` 可以看这里 [声明方法的属性](https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html)


##### isSignature:compatibleWithSignature:


在这个类中最后一个重要的方法就是 `isSignature:compatibleWithSignature:`,这个方法是判断传入的 block 和方法的类型签名是否兼容。


```objectivec

+ (BOOL)isSignature:(NSMethodSignature *)signatureA compatibleWithSignature:(NSMethodSignature *)signatureB __attribute__((pure))

{

#1: 参数检查,省略

...

#2: 判断返回值是否相同,省略

if (signatureA.methodReturnType[0] != signatureB.methodReturnType[0]) return NO;

#3: 设置 methodSignature blockSignature

...

#4: 比较 methodSignature blockSignature


return YES;

}

```


`#3` 部分设置 `methodSignature` `blockSignature`


因为**方法签名会比 block 类型签名多一个默认参数**,所以,这里会将参数多的设置为 `methodSignature`,如果把为 block 类型签名的设置给了 `methodSignature` 也不会有问题,在 `#4` 部分会判断出来并返回 `NO`


> 方法默认参数:`selfSEL`block 默认类型参数: `block`


```objectivec

NSMethodSignature *methodSignature = nil*blockSignature = nil;

if (signatureA.numberOfArguments > signatureB.numberOfArguments) {

    methodSignature = signatureA;

    blockSignature = signatureB;

} else if (signatureB.numberOfArguments > signatureA.numberOfArguments) {

    methodSignature = signatureB;

    blockSignature = signatureA;

} else {

    return NO;

}

```


`#4` 部分就是一次比较各个类型签名,也没什么复杂的,需要注意的就是选择正确的 index


```objectivec

NSUInteger numberOfArguments = methodSignature.numberOfArguments;

for (NSUInteger i = 2; i < numberOfArguments; i++) {

    if ([methodSignature getArgumentTypeAtIndex:i][0] != [blockSignature getArgumentTypeAtIndex:i - 1][0])

        return NO;

}

```


##### invokeWithInvocation:returnValue:outReturnValue:


这一节主要介绍的是,当 `A2BlockInvocation` 对象具体需要执行某一个 `NSInvocation` 时是如何工作的,其实这个方法还是很容易理解的。


```objectivec

- (BOOL)invokeWithInvocation:(NSInvocation *)outerInv returnValue:(out NSValue **)outReturnValue setOnInvocation:(BOOL)setOnInvocation

{

#1: 参数以及类型签名是否匹配的检查,省略


NSInvocation *innerInv = [NSInvocation invocationWithMethodSignature:self.blockSignature];


#2: 设置 innerInv 参数

...


[innerInv invokeWithTarget:self.block];


#3: 获取返回值


free(argBuf);


return YES;

}

```


`#2``#3` 部分的代码是为了设置 `innerInv` 的参数,获取返回值:


```objectivec

void *argBuf = NULL;


for (NSUInteger i = 2; i < sig.numberOfArguments; i++) {

const char *type = [sig getArgumentTypeAtIndex:i];

NSUInteger argSize;

NSGetSizeAndAlignment(type&argSizeNULL);


if (!(argBuf = reallocf(argBufargSize))) {

return NO;

}


[outerInv getArgument:argBuf atIndex:i];

[innerInv setArgument:argBuf atIndex:i - 1];

}


// 执行 block


NSUInteger retSize = sig.methodReturnLength;

if (retSize) {

    if (outReturnValue || setOnInvocation) {

        if (!(argBuf = reallocf(argBufretSize))) {

            return NO;

        }


        [innerInv getReturnValue:argBuf];


        if (setOnInvocation) {

            [outerInv setReturnValue:argBuf];

        }


        if (outReturnValue) {

            *outReturnValue = [NSValue valueWithBytes:argBuf objCType:sig.methodReturnType];

        }

    }

} else {

    if (outReturnValue) {

        *outReturnValue = nil;

    }

}

```


`A2BlockInvocation` 这一节就到这里了,接下来要说一下 `A2DynamicDelegate`


#### A2DynamicDelegate


`A2DynamicDelegate` 可以说是 BlocksKit 实现动态代理的关键,是这个框架中很重要的类,它通过 block 实现了类的代理和数据源等协议。


`A2DynamicDelegate` 它的父类是 `NSProxy`,而 `NSProxy` 出现的目的就是为了代理一个对象的。


```objectivec

@interface NSProxy <NSObject>

```


我们不具体解释这里的 `NSProxy`,如果想要更详细的信息,请看[这里](https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSProxy_Class/index.html)


`A2DynamicDelegate` 作为 `NSProxy` 的子类,必须实现 `forwardInvocation:` `methodSignatureForSelector:` 方法进行对象转发,这是在苹果官方文档中说明的。


##### 覆写必要的方法 methodSignatureForSelector: forwardInvocation:


我们首先来看一下 `methodSignatureForSelector:`,它为一个选择子返回合适的方法签名:


```objectivec

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

{

A2BlockInvocation *invocation = nil;

if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector]))

return invocation.methodSignature;

else if ([self.realDelegate methodSignatureForSelector:aSelector])

return [self.realDelegate methodSignatureForSelector:aSelector];

else if (class_respondsToSelector(object_getClass(self)aSelector))

return [object_getClass(self) methodSignatureForSelector:aSelector];

return [[NSObject class] methodSignatureForSelector:aSelector];

}

```


这里的逻辑如下:


1. 判断 `invocationsBySelectors` 属性中是否存储了该选择子对应的 `A2BlockInvocation`,直接返回这个 `invocation` 对象的类型签名,也就是说自己实现了该选择子对应的方法

2. 在真正的 `realDelegate` 中查找原有的代理(不是当前的动态代理 `A2DynamicDelegate`)是否实现了该选择子,并返回方法签名


> 在这里的 `realDelegate` 是对象真正的代理,例如 

```objectivec

self.tableView.delegate = [[UIViewController alloc] init];

```

其中 `realDelegate` 是视图控制器,但是在我们设置时,不需要这么设置 

```objectivec

self.tableView.realDelegate = [[UIViewController alloc] init];

```

因为在 `NSObject+A2BlockDelegate` 中会进行**方法调剂**,修改原有方法的实现,每次在设置 `delegate` 时,会将这个值设置传到 `realDelegate` 中。

3. 在自己的类中查找该方法的选择子

4. 如果上面三个步骤都没有得到相应,那么调用 `NSObject` 对象的 `methodSignatureForSelector:` 方法获取方法签名,当然可能返回空值


====


`forwardInvocation:` 的实现其实跟上面的方法的思路差不多


```objectivec

- (void)forwardInvocation:(NSInvocation *)outerInv

{

SEL selector = outerInv.selector;

A2BlockInvocation *innerInv = nil;

if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {

[innerInv invokeWithInvocation:outerInv];

} else if ([self.realDelegate respondsToSelector:selector]) {

[outerInv invokeWithTarget:self.realDelegate];

}

}

```


1. 判断 `invocationsBySelectors` 属性中是否存储了该选择子对应的 `A2BlockInvocation`,然后调用 `invokeWithInvocation:` 传入 `outerInv` 转发这个方法,最终会调用 `- [A2BlockInvocation invokeWithInvocation:returnValue:setOnInvocation:]

`


2. 判断 `realDelegate` 是否实现了该方法,如果真正的代理能做出响应,将方法转发给 `realDelegate`


##### Block 实现方法 blockImplementationForMethod: implementMethod:withBlock:


这部分的代码其实相当于平时的 Getter/Setter 


```objectivec

- (id)blockImplementationForMethod:(SEL)selector

{

A2BlockInvocation *invocation = nil;

if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector]))

return invocation.block;

return NULL;

}

```


因为 block 都是在 `A2BlockInvocation` 中封装的,所以在通过选择子查找 block 的时候,实际上是查找对应的 `A2BlockInvocation`,然后返回它的 block


```objectivec

- (void)implementMethod:(SEL)selector withBlock:(id)block

{


#1: 参数检查,省略


if (!block) {

[self.invocationsBySelectors bk_removeObjectForSelector:selector];

return;

}

#2: 实例化 A2BlockInvocation


[self.invocationsBySelectors bk_setObject:inv forSelector:selector];

}

```


如果能获取到方法的描述,那么就可以得到对应的方法签名,然后调用不同的初始化方法实例一个 `A2Blockinvocation` 对象。


```objectivec

struct objc_method_description methodDescription = protocol_getMethodDescription(self.protocolselectorYES!isClassMethod);

if (!methodDescription.name) methodDescription = protocol_getMethodDescription(self.protocolselectorNO!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];

}

```


这两个方法的实现,主要目的是**为子类实现代理方法提供支持**


#### NSObject+A2DynamicDelegate 为对象添加动态代理


这个分类是为所有的对象提供简单快捷的接口找到对应的动态代理:


```objectivec

@property (readonlystrong) id bk_dynamicDataSource;

@property (readonlystrong) id bk_dynamicDelegate;

- (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol;

```


`UITableView` 为例:


+ 访问 `tableView.bk_dynamicDataSource` 那么它就会寻找 `A2DynamicUITableViewDataSource` 的对象

+ 访问 `tableView.bk_dynamicDelegate` 那么它就会寻找 `A2DynamicUITableViewDelegate` 的对象


这些对象都是在后台进程中惰性初始化的:


```objectivec

- (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 *)protocoldynamicDelegateOBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

});


return dynamicDelegate;

}

```


#### NSObject+A2BlockDelegate


我们在概述的一部分实际上已经接触过这个分类里面的重要方法 `bk_linkProtocol:methods:`,它动态实现所有添加的 block 属性的存取方法,比如说 `bk_didFinishPickingMediaBlock` `bk_didCancelBlock`


```objectivec

IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {

A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObjectprotocolinfoNO);

return [delegate blockImplementationForMethod:selector];

});


IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObjectid block) {

A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObjectprotocolinfoYES);

[delegate implementMethod:selector withBlock:block];

});

```


方法调剂之后的存取方法如下


+ getter: selector 为键在动态代理中查找对应的 block

+ setter: selector 也就是代理方法为键,通过 `implementMethod:withBlock:` 方法以 `A2BlockInvocation` 的形式存储 block


另一个方法 `bk_registerDynamicDelegateNamed:forProtocol:`,它主要功能就是修改 getter setter 方法,将原有的 `delegate` 转发到 `realDelegate`,修改原有的 `delegate` 的实现,实现的方法就是喜闻乐见的方法调节:


```objectivec

IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObjectid delegate) {

A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObjectprotocolinfoAsPtrYES);

if ([delegate isEqual:dynamicDelegate]) {

delegate = nil;

}

dynamicDelegate.realDelegate = delegate;

});


IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {

return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObjectprotocol)];

});

```


注意,在这里省略了一些与脉络无关的实现细节,在调剂过后 `delegate` 的存取方法如下:


+ getter:返回一个动态代理对象

+ setter:设置代理并不会改变 `delegate` 中存储的动态代理,只会修改 `realDelegate`


我们现在有了通过 runtime 实现 block getter/setter,修改原有的 `delegate` 属性的方法将对象的代理设置为动态代理,接下来要在子类化动态代理,使用动态代理的子类实现所有的代理方法。


#### A2DynamicUITextFieldDelegate


`A2DynamicUITextFieldDelegate` `UITextField+BlocksKit` 位于统一文件下,它是一个私有类,我们选取其中一个简单的代理方法:


```objectivec

- (void)textFieldDidEndEditing:(UITextField *)textField

{

id realDelegate = self.realDelegate;

if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidEndEditing:)])

[realDelegate textFieldDidEndEditing:textField];

void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];

if (block)

block(textField);

}

```


1. `realDelegate` 实现了该代理方法时,首先调用代理的方法

2. 当该代理方法对应的 block 存在的话,也会调用该 block




#### UITextField+BlocksKit 分类和 load 方法


在最后就是对 `NSObject+A2BlockDelegate` 分类中方法的调用


```objectivec

+ (void)load {

    [self bk_registerDynamicDelegate];

    [self bk_linkDelegateMethods: @{

        @"bk_shouldBeginEditingBlock": @"textFieldShouldBeginEditing:",

        @"bk_didBeginEditingBlock": @"textFieldDidBeginEditing:",

        @"bk_shouldEndEditingBlock": @"textFieldShouldEndEditing:",

        @"bk_didEndEditingBlock" : @"textFieldDidEndEditing:",

        @"bk_shouldChangeCharactersInRangeWithReplacementStringBlock" : @"textField:shouldChangeCharactersInRange:replacementString:",

        @"bk_shouldClearBlock" : @"textFieldShouldClear:",

        @"bk_shouldReturnBlock" : @"textFieldShouldReturn:",

    }];

}

```


为什么在 `load` 方法中调用这两个方法?原因有两个:


+ 每个文件中的该方法都只会调用一次,减少了调用的次数

+ 该方法只会在文件被引入 `ObjC 运行时` 的时候调用


其中的 `autoreleasepool` 的作用在上面已经介绍过了,它使得其它地方的代码不会受到这里注册代理,链接代理方法中产生的对象的影响。


`UIKit+BlocksKit` 这些分类的另一作用就是提供 block 回调接口,声明属性,然后使用 `@dynamic` 表明属性是动态生成的。


```objectivec

@property (nonatomiccopynullable) BOOL(^bk_shouldBeginEditingBlock)(UITextField *textField);

@property (nonatomiccopynullable) void(^bk_didBeginEditingBlock)(UITextField *textField);


...


@dynamic bk_shouldBeginEditingBlockbk_didBeginEditingBlock ...;

```


## End


到这里对于 BlocksKit 的实现机制就基本上已经看完了。我们再来看一下 整个 BlocksKit 的结构图:


![](../images/blockskit.png)


我写这篇文章大约用了七天的时间,如果你对其中的内容有些疑问,可以发邮件或者在下面留言。


+ [神奇的 BlocksKit(一)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/BlocksKit/神奇的%20BlocksKit%20(一).md)

+ [神奇的 BlocksKit(二)](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/BlocksKit/神奇的%20BlocksKit%20(二).md)


<iframe src="http://ghbtns.com/github-btn.html?user=draveness&type=follow&size=large" height="30" width="240" frameborder="0" scrolling="0" style="width:240px; height: 30px;" allowTransparency="true"></iframe>


Follow: [@Draveness](https://github.com/Draveness)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值