| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
hubuke
8年前发布

神奇的 BlocksKit (二)

   <p>这篇文章『神奇的 BlocksKit』的第二部分,关于第一部分的内容在这里:</p>    <p>  </p>   <ul>     <li><a href="http://www.open-open.com/lib/view/open1464271189490.html">神奇的 BlocksKit(一)</a></li>     <li><a href="http://www.open-open.com/lib/view/open1464271436361.html">神奇的 BlocksKit(二)</a></li>    </ul>    <p></p>    <h2>动态代理</h2>    <p>动态代理这部分可以说是 BlocksKit 的精华。它<strong>使用 block 属性替换 UIKit 中的所有能够通过代理完成的事件</strong>,省略了设置代理和实现方法的过程,让对象自己实现代理方法,而且这个功能的实现是极其动态的。</p>    <blockquote>     <p>其实不是对象自己实现的代理方法,只是框架为我们提供的便捷方法,不需要构造其它对象就能完成代理方法的实现,具体我们会在后面详细地解释。</p>    </blockquote>    <p>下面是这部分几个关键的类:</p>    <ul>     <li><code>A2BlockInvocation</code> 的主要作用是存储和转发 block</li>     <li><code>A2DynamicDelegate</code> 用来实现类的代理和数据源,它是 <code>NSProxy</code> 的子类</li>     <li><code>NSObject+A2DynamicDelegate</code> 负责为返回 <code>bk_dynamicDelegate</code> 和 <code>bk_dynamicDataSource</code> 等 <code>A2DynamicDelegate</code> 类型的实例,为 <code>NSObject</code> 提供主要的接口</li>     <li><code>NSObject+A2BlockDelegate</code> 提供了一系列接口将代理方法映射到 block 上</li>     <li>其他的 UIKit 的分类提供对应的属性,并在对应的 <code>A2DynamicDelegate</code> 子类中实现代理方法</li>    </ul>    <p>这里是我对这部分代码结构的理解:</p>    <p><img alt="神奇的 BlocksKit (二)" src="https://simg.open-open.com/show/365c40286ca775efba346c143f6697f0.png"></p>    <p>这篇文成首先会从上到下对整个工作原理进行概述,然后再从底层到顶层详细地解释这个框架的机制和原理。</p>    <h3>动态代理工作概述</h3>    <p>在这里我们要对这部分的实现进行一个简单的概述,从上到下跟踪 BlocksKit 的调用过程。</p>    <p>以 <code>UIImagePickerController</code> 为例,因为这个文件中的代码较少,能省去很多不必要的实现细节。</p>    <p>在头文件中声明了两个属性,也就是 <code>UIImagePickerController</code> 代理方法的对应 block 属性:</p>    <pre>  <code class="language-objectivec">@property (nonatomic,copy) void(^bk_didFinishPickingMediaBlock)(UIImagePickerController *,NSDictionary *);  @property (nonatomic,copy) void(^bk_didCancelBlock)(UIImagePickerController *);  </code></pre>    <p>然后在实现文件中动态生成这两个方法的存取方法</p>    <pre>  <code class="language-objectivec">@dynamic bk_didFinishPickingMediaBlock;  @dynamic bk_didCancelBlock;  </code></pre>    <p>你可以看到在这个名为 <code>BlocksKit</code> 的分类中只添加了一个方法:</p>    <pre>  <code class="language-objectivec">+ (void)load  {      @autoreleasepool {          [self bk_registerDynamicDelegate];          [self bk_linkDelegateMethods:@{ @"bk_didFinishPickingMediaBlock": @"imagePickerController:didFinishPickingMediaWithInfo:",                                          @"bk_didCancelBlock": @"imagePickerControllerDidCancel:" }];      }  }  </code></pre>    <p>在 <code>load</code> 中实现这个方法,能够减少其中两个方法的调用次数,在 <code>autoreleasepool</code> 块中调用方法,使得其它地方的代码不会受到这里注册代理,链接代理方法中产生的对象的影响。</p>    <p><code>bk_registerDynamicDelegate</code> 方法是 <code>NSObject+A2BlockDelegate</code> 分类中添加的方法,用于修改原有属性 <code>delegate</code> 方法的实现(<strong>动态替换 delegate 方法实现</strong>)。在这里就是与 <code>UIImagePickerController+BlocksKit</code> 处于同一文件下的 <code>A2DynamicUIImagePickerControllerDelegate</code>,先不说这个文件的功能,会在之后介绍。</p>    <p>在 <code>NSObject+A2DynamicDelegate</code> 分类中的 <code>bk_registerDynamicDelegateNamed:forProtocol:</code> 修改 <code>@selector(delegate)</code> 和 <code>@selector(setDelegate:)</code> 的实现,使用 <code>A2DynamicUIImagePickerControllerDelegate</code> 替换原有的 <code>delegate</code></p>    <pre>  <code class="language-objectivec">IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject,id delegate) {        A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject,protocol,infoAsPtr,YES);      if ([delegate isEqual:dynamicDelegate]) {          delegate = nil;      }      dynamicDelegate.realDelegate = delegate;  });    IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {        return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject,protocol)];  });  </code></pre>    <p>在获取 <code>delegate</code> 属性时,就会获取 <code>A2DynamicUIImagePickerControllerDelegate</code>, <code>realDelegate</code> 相当于原有的 <code>delegate</code> 属性,会在下面的小节中具体分析。</p>    <p>在 <code>load</code> 方法中调用下一个方法是 <code>bk_linkDelegateMethods:</code> 这个方法会把代理方法和对应的 block 属性链接起来,这样可以通过代理方法的选择子查找对应的 block。</p>    <pre>  <code class="language-objectivec">IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {        A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject,protocol,info,NO);      return [delegate blockImplementationForMethod:selector];  });    IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject,id block) {        A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject,protocol,info,YES);      [delegate implementMethod:selector withBlock:block];  });  </code></pre>    <p>通过调用 <code>A2DynamicDelegate</code> 的实例方法 <code>blockImplementationForMethod:</code> 和 <code>implementMethod:withBlock:</code> 动态实现 block 的存取方法。</p>    <p>当代理方法 <code>imagePickerController:didFinishPickingMediaWithInfo:</code> 被调用时,因为 <code>A2DynamicUIImagePickerControllerDelegate</code> 是 <code>UIImagePickerController</code> 的代理,所以会调用它的方法:</p>    <pre>  <code class="language-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(picker,info);  }  </code></pre>    <p>通过 <code>blockImplementationForMethod:</code> 方法获取在上面存储的 block,然后传入参数执行该代码块。</p>    <ol>     <li>在 <code>load</code> 方法注册动态代理并链接代理方法</li>     <li>在运行时修改原有的 <code>delegate</code> 属性的存取方法,使用 <code>A2DynamicDelegate</code> 替换原有的 <code>delegate</code>,原有的 <code>delegate</code> 换为 <code>realDelegate</code></li>     <li>为 block 属性动态实现存取方法,返回对应 <code>A2DynamicDelegate</code> 子类中存储的 block</li>     <li>在代理方法真正被调用时,查找 <code>realDelegate</code> 中是否对代理方法做出响应,无论是否响应,都通过选择子查找对应的 <code>block</code>,然后传入相应参数执行 block</li>    </ol>    <h3>自底向上分析动态代理的工作</h3>    <p>我们已经自顶向下分析了 BlocksKit 的工作过程,也对这个部分有一个基本的了解,接下来我们将从底层到顶层分析整个 BlocksKit,我们再来看一下整个框架的结构图:</p>    <p><img alt="神奇的 BlocksKit (二)" src="https://simg.open-open.com/show/365c40286ca775efba346c143f6697f0.png"></p>    <p>我们将以下面的顺序来依次介绍这些模块,其中的 <code>UITextField</code> 可以换成其它的类:</p>    <ol>     <li>A2BlockInvocation</li>     <li>A2DynamicDelegate</li>     <li>NSObject+A2DynamicDelegate</li>     <li>A2DynamicUITextFieldDelegate</li>     <li>UITextField+BlocksKit</li>    </ol>    <p>A2BlockInvocation</p>    <p><code>A2BlockInvocation</code> 使用来对闭包,也就是 block 进行存储和转发的类。</p>    <p>先介绍这个的是因为 <code>A2BlockInvocation</code> 的功能比较底层,涉及的内容也都比较奇葩,所以想先简单介绍一下,避免之后一个类分几部分介绍。</p>    <p>在 Objective-C 中,每一个方法甚至 block 都是有类型签名的:</p>    <pre>  <code class="language-objectivec">@interface NSMethodSignature : NSObject {    ...    @property (readonly) NSUInteger numberOfArguments;    ...    @property (readonly) const char *methodReturnType NS_RETURNS_INNER_POINTER;    ...    @end  </code></pre>    <p>它们有返回类型、参数数字和参数类型等等。</p>    <p>Block 结构体</p>    <p>block 的签名没有哪个函数能够直接获取,它存储在 block 的结构体中,就像这样:</p>    <pre>  <code class="language-objectivec">typedef NS_OPTIONS(int,BKBlockFlags) {        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 *dst,const void *src);          void (*dispose)(const void *);          // requires BKBlockFlagsHasSignature          const char *signature;          const char *layout;      } *descriptor;      // imported variables  } *BKBlockRef;  </code></pre>    <p>这部分其实就是 block 实际存储在内存中的数据接口,可以在 runtime 中的源代码中看到这里的代码。</p>    <p>typeSignatureForBlock</p>    <p>上面的 <code>signature</code> 就是 block 的签名,下面实现方法来获取这个签名</p>    <pre>  <code class="language-objectivec">+ (NSMethodSignature *)typeSignatureForBlock:(id)block __attribute__((pure,nonnull(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];  }  </code></pre>    <p>知道了这个方法的作用再理解它的实现就非常简单了,根据<code>flag</code> 来移动指针,最终 <code>signature</code> 所在的内存空间。</p>    <blockquote>     <p>Unlike a typical method signature,a block type signature has no <code>self</code> (<code>'@'</code>) or <code>_cmd</code> (<code>':'</code>) parameter,but instead just one parameter for the block itself (<code>'@?'</code>)。</p>    </blockquote>    <p>在这里所涉及的 <code>@</code>、<code>:</code> 和<code>@?</code> 可以看这里的文档 <a href="/misc/goto?guid=4959673792487564503">类型编码</a></p>    <p>在一般的方法签名中 block 的类型签名是没有 <code>self</code> (<code>'@'</code>) 或者 <code>_cmd</code> (<code>':'</code>) 的,只有一个参数代表 block 自己 (<code>'@?'</code>).</p>    <ul>     <li><code>^(UIActionSheet *) {}</code>      <ul>       <li>参数类型:<code>@?(@"UIActionSheet")</code></li>       <li>返回类型:<code>v</code></li>      </ul> </li>     <li><code>- (void)willPresentActionSheet:(UIActionSheet *)actionSheet</code>      <ul>       <li>参数类型:<code>@:@</code></li>       <li>返回类型:<code>v</code></li>      </ul> </li>    </ul>    <p>为什么要把 <code>@"UIActionSheet"</code> 标记上括号?因为它们属于同一个参数。</p>    <p>同时因为 <code>UIActionSheet</code> 也是 <code>id</code> 类型,所以它的类型编码也是 <code>@</code>。</p>    <p>当调用 <code>initWithBlock:</code> 方法时,会先调用上面说的方法 <code>typeSignatureForBlock:</code> 获取 block 的类型签名:</p>    <pre>  <code class="language-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]);  }  </code></pre>    <p>methodSignatureForBlockSignature</p>    <p>然后调用 <code>methodSignatureForBlockSignature:</code> 方法构造一个可以兼容的方法签名:</p>    <pre>  <code class="language-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];  }  </code></pre>    <p>具体的实现细节我们就省略了,它的工作原理是把 <code>@?(@"UIActionSheet")</code> 类型签名转换成 <code>@:@</code>,然后返回方法签名。</p>    <p>关于代码中的 <code>@encode</code> 可以看这里 <a href="/misc/goto?guid=4959673792583754535">声明方法的属性</a></p>    <p>isSignature:compatibleWithSignature:</p>    <p>在这个类中最后一个重要的方法就是 <code>isSignature:compatibleWithSignature:</code>,这个方法是判断传入的 block 和方法的类型签名是否兼容。</p>    <pre>  <code class="language-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;  }  </code></pre>    <p>第 <code>#3</code> 部分设置 <code>methodSignature</code> 和 <code>blockSignature</code>。</p>    <p>因为<strong>方法签名会比 block 类型签名多一个默认参数</strong>,所以,这里会将参数多的设置为 <code>methodSignature</code>,如果把为 block 类型签名的设置给了 <code>methodSignature</code> 也不会有问题,在 <code>#4</code> 部分会判断出来并返回 <code>NO</code>。</p>    <blockquote>     <p>方法默认参数:<code>self,SEL</code>,block 默认类型参数: <code>block</code></p>    </blockquote>    <pre>  <code class="language-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;  }  </code></pre>    <p>第 <code>#4</code> 部分就是一次比较各个类型签名,也没什么复杂的,需要注意的就是选择正确的 index</p>    <pre>  <code class="language-objectivec">NSUInteger numberOfArguments = methodSignature.numberOfArguments;    for (NSUInteger i = 2; i < numberOfArguments; i++) {        if ([methodSignature getArgumentTypeAtIndex:i][0] != [blockSignature getArgumentTypeAtIndex:i - 1][0])          return NO;  }  </code></pre>    <p>invokeWithInvocation:returnValue:outReturnValue:</p>    <p>这一节主要介绍的是,当 <code>A2BlockInvocation</code> 对象具体需要执行某一个 <code>NSInvocation</code> 时是如何工作的,其实这个方法还是很容易理解的。</p>    <pre>  <code class="language-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;  }  </code></pre>    <p>第 <code>#2</code>、<code>#3</code> 部分的代码是为了设置 <code>innerInv</code> 的参数,获取返回值:</p>    <pre>  <code class="language-objectivec">void *argBuf = NULL;    for (NSUInteger i = 2; i < sig.numberOfArguments; i++) {        const char *type = [sig getArgumentTypeAtIndex:i];      NSUInteger argSize;      NSGetSizeAndAlignment(type,&argSize,NULL);        if (!(argBuf = reallocf(argBuf,argSize))) {          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(argBuf,retSize))) {              return NO;          }            [innerInv getReturnValue:argBuf];            if (setOnInvocation) {              [outerInv setReturnValue:argBuf];          }            if (outReturnValue) {              *outReturnValue = [NSValue valueWithBytes:argBuf objCType:sig.methodReturnType];          }      }  } else {      if (outReturnValue) {          *outReturnValue = nil;      }  }  </code></pre>    <p><code>A2BlockInvocation</code> 这一节就到这里了,接下来要说一下 <code>A2DynamicDelegate</code>。</p>    <p>A2DynamicDelegate</p>    <p><code>A2DynamicDelegate</code> 可以说是 BlocksKit 实现动态代理的关键,是这个框架中很重要的类,它通过 block 实现了类的代理和数据源等协议。</p>    <p><code>A2DynamicDelegate</code> 它的父类是 <code>NSProxy</code>,而 <code>NSProxy</code> 出现的目的就是为了代理一个对象的。</p>    <pre>  <code class="language-objectivec">@interface NSProxy <NSObject>  </code></pre>    <p>我们不具体解释这里的 <code>NSProxy</code>,如果想要更详细的信息,请看<a href="/misc/goto?guid=4959673792672240404">这里</a>。</p>    <p><code>A2DynamicDelegate</code> 作为 <code>NSProxy</code> 的子类,必须实现 <code>forwardInvocation:</code> <code>methodSignatureForSelector:</code> 方法进行对象转发,这是在苹果官方文档中说明的。</p>    <p>覆写必要的方法 methodSignatureForSelector: 和 forwardInvocation:</p>    <p>我们首先来看一下 <code>methodSignatureForSelector:</code>,它为一个选择子返回合适的方法签名:</p>    <pre>  <code class="language-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];  }  </code></pre>    <p>这里的逻辑如下:</p>    <ol>     <li>判断 <code>invocationsBySelectors</code> 属性中是否存储了该选择子对应的 <code>A2BlockInvocation</code>,直接返回这个 <code>invocation</code> 对象的类型签名,也就是说自己实现了该选择子对应的方法</li>     <li> <p>在真正的 <code>realDelegate</code> 中查找原有的代理(不是当前的动态代理 <code>A2DynamicDelegate</code>)是否实现了该选择子,并返回方法签名</p>      <blockquote>       <p>在这里的 <code>realDelegate</code> 是对象真正的代理,例如</p>      </blockquote> <p><code>objectivec self.tableView.delegate = [[UIViewController alloc] init]; </code></p> <p>其中 <code>realDelegate</code> 是视图控制器,但是在我们设置时,不需要这么设置</p> <p><code>objectivec self.tableView.realDelegate = [[UIViewController alloc] init]; </code></p> <p>因为在 <code>NSObject+A2BlockDelegate</code> 中会进行<strong>方法调剂</strong>,修改原有方法的实现,每次在设置 <code>delegate</code> 时,会将这个值设置传到 <code>realDelegate</code> 中。</p> </li>     <li> <p>在自己的类中查找该方法的选择子</p> </li>     <li>如果上面三个步骤都没有得到相应,那么调用 <code>NSObject</code> 对象的 <code>methodSignatureForSelector:</code> 方法获取方法签名,当然可能返回空值</li>    </ol>    <p>====</p>    <p><code>forwardInvocation:</code> 的实现其实跟上面的方法的思路差不多</p>    <pre>  <code class="language-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];      }  }  </code></pre>    <ol>     <li> <p>判断 <code>invocationsBySelectors</code> 属性中是否存储了该选择子对应的 <code>A2BlockInvocation</code>,然后调用 <code>invokeWithInvocation:</code> 传入 <code>outerInv</code> 转发这个方法,最终会调用 <code>- [A2BlockInvocation invokeWithInvocation:returnValue:setOnInvocation:] </code></p> </li>     <li> <p>判断 <code>realDelegate</code> 是否实现了该方法,如果真正的代理能做出响应,将方法转发给 <code>realDelegate</code></p> </li>    </ol>    <p>Block 实现方法 blockImplementationForMethod: 和 implementMethod:withBlock:</p>    <p>这部分的代码其实相当于平时的 Getter/Setter</p>    <pre>  <code class="language-objectivec">- (id)blockImplementationForMethod:(SEL)selector  {      A2BlockInvocation *invocation = nil;      if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector]))          return invocation.block;      return NULL;  }  </code></pre>    <p>因为 block 都是在 <code>A2BlockInvocation</code> 中封装的,所以在通过选择子查找 block 的时候,实际上是查找对应的 <code>A2BlockInvocation</code>,然后返回它的 block。</p>    <pre>  <code class="language-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];  }  </code></pre>    <p>如果能获取到方法的描述,那么就可以得到对应的方法签名,然后调用不同的初始化方法实例一个 <code>A2Blockinvocation</code> 对象。</p>    <pre>  <code class="language-objectivec">    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];      }  </code></pre>    <p>这两个方法的实现,主要目的是<strong>为子类实现代理方法提供支持</strong>。</p>    <p>NSObject+A2DynamicDelegate 为对象添加动态代理</p>    <p>这个分类是为所有的对象提供简单快捷的接口找到对应的动态代理:</p>    <pre>  <code class="language-objectivec">@property (readonly,strong) id bk_dynamicDataSource;  @property (readonly,strong) id bk_dynamicDelegate;  - (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol;  </code></pre>    <p>以 <code>UITableView</code> 为例:</p>    <ul>     <li>访问 <code>tableView.bk_dynamicDataSource</code> 那么它就会寻找 <code>A2DynamicUITableViewDataSource</code> 的对象</li>     <li>访问 <code>tableView.bk_dynamicDelegate</code> 那么它就会寻找 <code>A2DynamicUITableViewDelegate</code> 的对象</li>    </ul>    <p>这些对象都是在后台进程中惰性初始化的:</p>    <pre>  <code class="language-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 *)protocol,dynamicDelegate,OBJC_ASSOCIATION_RETAIN_NONATOMIC);          }      });        return dynamicDelegate;  }  </code></pre>    <p>NSObject+A2BlockDelegate</p>    <p>我们在概述的一部分实际上已经接触过这个分类里面的重要方法 <code>bk_linkProtocol:methods:</code>,它动态实现所有添加的 block 属性的存取方法,比如说 <code>bk_didFinishPickingMediaBlock</code> <code>bk_didCancelBlock</code></p>    <pre>  <code class="language-objectivec">        IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {              A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject,protocol,info,NO);              return [delegate blockImplementationForMethod:selector];          });            IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject,id block) {              A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject,protocol,info,YES);              [delegate implementMethod:selector withBlock:block];          });  </code></pre>    <p>方法调剂之后的存取方法如下</p>    <ul>     <li>getter: 以 selector 为键在动态代理中查找对应的 block</li>     <li>setter: 以 selector 也就是代理方法为键,通过 <code>implementMethod:withBlock:</code> 方法以 <code>A2BlockInvocation</code> 的形式存储 block</li>    </ul>    <p>另一个方法 <code>bk_registerDynamicDelegateNamed:forProtocol:</code>,它主要功能就是修改 getter 和 setter 方法,将原有的 <code>delegate</code> 转发到 <code>realDelegate</code>,修改原有的 <code>delegate</code> 的实现,实现的方法就是喜闻乐见的方法调节:</p>    <pre>  <code class="language-objectivec">    IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject,id delegate) {          A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject,protocol,infoAsPtr,YES);          if ([delegate isEqual:dynamicDelegate]) {              delegate = nil;          }          dynamicDelegate.realDelegate = delegate;      });        IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {          return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject,protocol)];      });  </code></pre>    <p>注意,在这里省略了一些与脉络无关的实现细节,在调剂过后 <code>delegate</code> 的存取方法如下:</p>    <ul>     <li>getter:返回一个动态代理对象</li>     <li>setter:设置代理并不会改变 <code>delegate</code> 中存储的动态代理,只会修改 <code>realDelegate</code></li>    </ul>    <p>我们现在有了通过 runtime 实现 block 的 getter/setter,修改原有的 <code>delegate</code> 属性的方法将对象的代理设置为动态代理,接下来要在子类化动态代理,使用动态代理的子类实现所有的代理方法。</p>    <p>A2DynamicUITextFieldDelegate</p>    <p><code>A2DynamicUITextFieldDelegate</code> 和 <code>UITextField+BlocksKit</code> 位于统一文件下,它是一个私有类,我们选取其中一个简单的代理方法:</p>    <pre>  <code class="language-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);  }  </code></pre>    <ol>     <li>当 <code>realDelegate</code> 实现了该代理方法时,首先调用代理的方法</li>     <li>当该代理方法对应的 block 存在的话,也会调用该 block</li>    </ol>    <p>UITextField+BlocksKit 分类和 load 方法</p>    <p>在最后就是对 <code>NSObject+A2BlockDelegate</code> 分类中方法的调用</p>    <pre>  <code class="language-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:",      }];  }  </code></pre>    <p>为什么在 <code>load</code> 方法中调用这两个方法?原因有两个:</p>    <ul>     <li>该方法只会调用一次,减少了调用的次数</li>     <li>该方法只会在文件被引用的时候调用,减少了不必要的动态代理注册等一系列步骤</li>    </ul>    <p>其中的 <code>autoreleasepool</code> 的作用在上面已经介绍过了,它使得其它地方的代码不会受到这里注册代理,链接代理方法中产生的对象的影响。</p>    <p><code>UIKit+BlocksKit</code> 这些分类的另一作用就是提供 block 回调接口,声明属性,然后使用 <code>@dynamic</code> 表明属性是动态生成的。</p>    <pre>  <code class="language-objectivec">@property (nonatomic,copy,nullable) BOOL(^bk_shouldBeginEditingBlock)(UITextField *textField);  @property (nonatomic,copy,nullable) void(^bk_didBeginEditingBlock)(UITextField *textField);    ...    @dynamic bk_shouldBeginEditingBlock,bk_didBeginEditingBlock ...;  </code></pre>    <h2>End</h2>    <p>到这里对于 BlocksKit 的实现机制就基本上已经看完了。我们在来看一下 整个 BlocksKit 的结构图:</p>    <p><img alt="神奇的 BlocksKit (二)" src="https://simg.open-open.com/show/365c40286ca775efba346c143f6697f0.png"></p>    <p>我写这篇文章大约用了七天的时间,如果你对其中的内容有些疑问,可以发邮件或者在下面留言。</p>    <ul>     <li><a href="http://www.open-open.com/lib/view/open1464271189490.html">神奇的 BlocksKit(一)</a></li>     <li><a href="http://www.open-open.com/lib/view/open1464271436361.html">神奇的 BlocksKit(二)</a></li>    </ul>    <p>来自:http://draveness.me/blockskit-2/</p>    
 本文由用户 hubuke 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1464271436361.html
iOS开发 Objective-C开发