简介

BlocksKit 是一个开源的框架,对 Cocoa 进行了扩展,将许多需要通过 delegate 调用的方法转换成了 block。在很多情况下,blocks 比 delegate 要方便简单,因为 block 是紧凑的,可以使代码简洁,提高代码可读性,另外 block 还可以进行异步处理。使用 block 要注意避免循环引用。

目录结构

BlocksKit 的所有方法都以bk_开头,这样可以方便地列出所有 BlocksKit 的所有方法。BlocksKit 主要目录结构

  • Core:存放 Foundation 相关的 Block category,如 NSObject、NSTimer、NSarray、NSDictionary、NSSet、NSIndexSet、NSMutableArray等
  • DynamicDelegate:动态代理(消息转发机制)
  • UIKit:扩展了 UIAlertView,UIActionView,UIButton 等

最常用的是 UIKit Category,它为 UIAlertView,UIActionSheet,UIButton,UITapGestureRecognizer 等提供了 blocks。

用法实例

UIAlertView 和 UIActionSheet 用法示例:

  1. UIAlertView *alertView = [[UIAlertView alloc] bk_initWithTitle:@"提示" message:@"提示信息"];
  2. [alertView bk_setCancelButtonWithTitle:@"取消" handler:nil];
  3. [alertView bk_addButtonWithTitle:@"确定" handler:nil];
  4. [alertView bk_setDidDismissBlock:^(UIAlertView *alert, NSInteger index) {
  5. if (index == 1) {
  6. NSLog(@"%ld clicked",index);
  7. }
  8. }];
  9. [alertView show];
  1. [[UIActionSheet bk_actionSheetCustomWithTitle:nil buttonTitles:@[@"查看", @"退出"] destructiveTitle:nil cancelTitle:@"取消" andDidDismissBlock:^(UIActionSheet *sheet, NSInteger index) {
  2. }] showInView:self.view];

UIButton 和 UITapGestureRecognizer 用法示例:

  1. UIButton *button = [[UIButton alloc] init];
  2. [button bk_addEventHandler:^(id sender) {
  3. } forControlEvents:UIControlEventTouchUpInside];
  1. UITapGestureRecognizer *tapGestureRecognizer = [UITapGestureRecognizer bk_recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
  2. if (state == UIGestureRecognizerStateRecognized) {
  3. ...
  4. }
  5. }];

UIButton 和 UIGesture 将 target-action 转换成 block,实现较简单:

  1. - (id)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay
  2. {
  3. self = [self initWithTarget:self action:@selector(bk_handleAction:)];
  4. if (!self) return nil;
  5. self.bk_handler = block;
  6. self.bk_handlerDelay = delay;
  7. return self;
  8. }
  9. - (void)bk_handleAction:(UIGestureRecognizer *)recognizer
  10. {
  11. void (^handler)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) = recognizer.bk_handler;
  12. if (!handler) return;
  13. ...
  14. if (!delay) {
  15. block();
  16. return;
  17. }
  18. ...
  19. }

delegate 转换成 block 实际上使用了消息转发机制,是 BlocksKit 源码中最难理解的部分。

原理分析: 消息转发机制

当一个对象收到它没实现的消息的时候,通常会发生如下的情况。

  1. 调用+(BOOL)resolveInstanceMethod:(SEL)aSEL,如果对象在这里动态添加了selector 的实现方法,则消息转发结束,否则执行步骤2
  2. 调用 - (id)forwardingTargetForSelector:(SEL)aSelector,在这里你可以将消息转发给其他对象,如果实现则消息转发结束,否则执行步骤3
  3. 执行完整的消息转发机制,调用-(void)forwardInvocation:(NSInvocation *)invocation 在这一步,你可以修改消息的任何内容,包括目标(target),selector,参数。如果没有实现在这里还未实现转发则程序将抛出异常。

原理实例分析

BlocksKit 动态代理实现方式是最后一步,即-(void)forwardInvocation:(NSInvocation *)invocation,使得动态代理能够接受任意消息。

以UIAlertView为例,UIAlertView在运行时动态关联了A2DynamicUIAlertViewDelegate

  1. @implementation UIAlertView (BlocksKit)
  2. @dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock, bk_shouldEnableFirstOtherButtonBlock;
  3. + (void)load
  4. {
  5. @autoreleasepool {
  6. [self bk_registerDynamicDelegate];
  7. [self bk_linkDelegateMethods:@{
  8. @"bk_willShowBlock": @"willPresentAlertView:",
  9. @"bk_didShowBlock": @"didPresentAlertView:",
  10. @"bk_willDismissBlock": @"alertView:willDismissWithButtonIndex:",
  11. @"bk_didDismissBlock": @"alertView:didDismissWithButtonIndex:",
  12. @"bk_shouldEnableFirstOtherButtonBlock": @"alertViewShouldEnableFirstOtherButton:"
  13. }];
  14. }
  15. }

A2DynamicUIAlertViewDelegate 是 A2DynamicDelegate 的子类,并实现了UIAlertViewDelegate 的方法

代理消息的转发由 A2DynamicDelegate 完成

  1. - (void)forwardInvocation:(NSInvocation *)outerInv
  2. {
  3. SEL selector = outerInv.selector;
  4. A2BlockInvocation *innerInv = nil;
  5. if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {
  6. [innerInv invokeWithInvocation:outerInv];
  7. } else if ([self.realDelegate respondsToSelector:selector]) {
  8. [outerInv invokeWithTarget:self.realDelegate];
  9. }
  10. }

注: 文章由我们 iOS122(http://www.ios122.com)的小伙伴 @鱼 整理,喜欢就一起参与: iOS122 任务池

BlocksKit初见:一个支持将delegate转换成block的Cocoa库的更多相关文章

  1. 使用 js 实现一个中文自动转换成拼音的工具库

    使用 js 实现一个中文自动转换成拼音的工具库 中文 => zhong-wen 应用场景 SEO 友好, URL 自动转换 blogs 发布文章,自动化部署,自动生成 url 的 path (时 ...

  2. 用c语言写一个函数把十进制转换成十六进制(转)

    #include "stdio.h" int main() { int num=0;int a[100]; int i=0; int m=0;int yushu; char hex ...

  3. Java中将一个反斜杠转换成两个反斜杠

    代码示例: s = s.replaceAll("\\\\", "\\\\\\\\");

  4. 56 Marvin: 一个支持GPU加速、且不依赖其他库(除cuda和cudnn)的轻量化多维深度学习(deep learning)框架介绍

    0 引言 Marvin是普林斯顿视觉实验室(PrincetonVision)于2015年提出的轻量化GPU加速的多维深度学习网络框架.该框架采用纯c/c++编写,除了cuda和cudnn以外,不依赖其 ...

  5. Java 里把 InputStream 转换成 String 的几种方法

    我们在 Java 中经常会碰到如何把 InputStream 转换成 String 的情形,比如从文件或网络得到一个 InputStream,需要转换成字符串输出或赋给别的变量. 未真正关注这个问题之 ...

  6. 自制 Python小工具 将markdown文件转换成Html文件

    今天看到了一个Python库,名为markdown.瞬间就给了我一个灵感,那就是制作一个将markdown文件转换成html文件的小工具. 我的实验环境 操作系统: Windows 7 64位 旗舰版 ...

  7. 字符串数字转换成对应的Double数值

    一,介绍 前面实现了字符串转换成整形数值.参考这里: 它不支持小数,不支持符号(正.负号) 现在实现一个更复杂一点字符串转换成数值的程序. 它支持“浮点字符串”转换成对应的浮点数值,如: " ...

  8. linux设备驱动程序-设备树(1)-dtb转换成device_node

    linux设备驱动程序-设备树(1)-dtb转换成device_node 本设备树解析基于arm平台 从start_kernel开始 linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂 ...

  9. Java 把 InputStream 转换成 String 的几种方法

    我们在 Java 中经常会碰到如何把 InputStream 转换成 String 的情形,比如从文件或网络得到一个 InputStream,需要转换成字符串输出或赋给别的变量. 未真正关注这个问题之 ...

随机推荐

  1. 奇怪吸引子---TreeScrollUnifiedChaoticSystem

    奇怪吸引子是混沌学的重要组成理论,用于演化过程的终极状态,具有如下特征:终极性.稳定性.吸引性.吸引子是一个数学概念,描写运动的收敛类型.它是指这样的一个集合,当时间趋于无穷大时,在任何一个有界集上出 ...

  2. Android判断当前线程是否是主线程的方法

    开发过程中有时候会在Thread类中执行某些操作,有些操作会由于Android版本的不同,尤其是低版本而Crash,因此必要的时候会查看某些容易引起crash的操作是否是在主线程,这里举三种方法: 方 ...

  3. callable object与新增的function相关 C++11中万能的可调用类型声明std::function<...>

    在c++11中,一个callable object(可调用对象)可以是函数指针.lambda表达式.重载()的某类对象.bind包裹的某对象等等,有时需要统一管理一些这几类对象,新增的function ...

  4. javascript - 二叉树

    都是些简单的东西,所以直接上代码了. /** * Created by huangjacky on 14-10-3. */ function Node(element, left, right) { ...

  5. Oracle中group by用法

    Oracle中group by用法 在select 语句中可以使用group by 子句将行划分成较小的组,一旦使用分组后select操作的对象变为各个分组后的数据,使用聚组函数返回的是每一个组的汇总 ...

  6. 关于execel单元格中的数字变成文本(左上角带绿色三角形标志)的办法

    对于很多软件,需要将数字变成文本,才能导入到该系统当中.在excel当中,如果数字是以文本的形式存储,在左上角是带有绿色的三角形标志的.如果对于大批量数据,操作方法如下:1将目标列数据copy到记事本 ...

  7. BpBinder 转换为 BpCameraService 流程

    interface_cast<ICameraService>(binder) : 其中binder 为IBinder类型,实际为BpBinder interface_cast 定义在IIn ...

  8. Oracle 一次生产分库,升级,迁移

    今天完成了一个负载较高的中央数据库的分库操作, 并实现了oracle的滚动升级(10.2.0.1->10.2.0.4), 业务中断仅15分钟. 平台: RHEL AS 4 + Oracle 10 ...

  9. T-SQL 小数点转换百分数

    -- ============================================= -- Author: <Author,,CC> -- Create date: <C ...

  10. CHECK MEMBER TYPE

    检查类里是否存在某种类型的几种方法,以检查xxx类型为例:方法1: template<class T> class has_member_type_Type { ]; }; templat ...