BlocksKit初见:一个支持将delegate转换成block的Cocoa库
简介
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 用法示例:
UIAlertView *alertView = [[UIAlertView alloc] bk_initWithTitle:@"提示" message:@"提示信息"];
[alertView bk_setCancelButtonWithTitle:@"取消" handler:nil];
[alertView bk_addButtonWithTitle:@"确定" handler:nil];
[alertView bk_setDidDismissBlock:^(UIAlertView *alert, NSInteger index) {
if (index == 1) {
NSLog(@"%ld clicked",index);
}
}];
[alertView show];
[[UIActionSheet bk_actionSheetCustomWithTitle:nil buttonTitles:@[@"查看", @"退出"] destructiveTitle:nil cancelTitle:@"取消" andDidDismissBlock:^(UIActionSheet *sheet, NSInteger index) {
}] showInView:self.view];
UIButton 和 UITapGestureRecognizer 用法示例:
UIButton *button = [[UIButton alloc] init];
[button bk_addEventHandler:^(id sender) {
} forControlEvents:UIControlEventTouchUpInside];
UITapGestureRecognizer *tapGestureRecognizer = [UITapGestureRecognizer bk_recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
if (state == UIGestureRecognizerStateRecognized) {
...
}
}];
UIButton 和 UIGesture 将 target-action 转换成 block,实现较简单:
- (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;
}
- (void)bk_handleAction:(UIGestureRecognizer *)recognizer
{
void (^handler)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) = recognizer.bk_handler;
if (!handler) return;
...
if (!delay) {
block();
return;
}
...
}
delegate 转换成 block 实际上使用了消息转发机制,是 BlocksKit 源码中最难理解的部分。
原理分析: 消息转发机制
当一个对象收到它没实现的消息的时候,通常会发生如下的情况。
- 调用
+(BOOL)resolveInstanceMethod:(SEL)aSEL,如果对象在这里动态添加了selector 的实现方法,则消息转发结束,否则执行步骤2 - 调用
- (id)forwardingTargetForSelector:(SEL)aSelector,在这里你可以将消息转发给其他对象,如果实现则消息转发结束,否则执行步骤3 - 执行完整的消息转发机制,调用
-(void)forwardInvocation:(NSInvocation *)invocation在这一步,你可以修改消息的任何内容,包括目标(target),selector,参数。如果没有实现在这里还未实现转发则程序将抛出异常。
原理实例分析
BlocksKit 动态代理实现方式是最后一步,即-(void)forwardInvocation:(NSInvocation *)invocation,使得动态代理能够接受任意消息。
以UIAlertView为例,UIAlertView在运行时动态关联了A2DynamicUIAlertViewDelegate
@implementation UIAlertView (BlocksKit)
@dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock, bk_shouldEnableFirstOtherButtonBlock;
+ (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:"
}];
}
}
A2DynamicUIAlertViewDelegate 是 A2DynamicDelegate 的子类,并实现了UIAlertViewDelegate 的方法

代理消息的转发由 A2DynamicDelegate 完成
- (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];
}
}
注: 文章由我们 iOS122(http://www.ios122.com)的小伙伴 @鱼 整理,喜欢就一起参与: iOS122 任务池
BlocksKit初见:一个支持将delegate转换成block的Cocoa库的更多相关文章
- 使用 js 实现一个中文自动转换成拼音的工具库
使用 js 实现一个中文自动转换成拼音的工具库 中文 => zhong-wen 应用场景 SEO 友好, URL 自动转换 blogs 发布文章,自动化部署,自动生成 url 的 path (时 ...
- 用c语言写一个函数把十进制转换成十六进制(转)
#include "stdio.h" int main() { int num=0;int a[100]; int i=0; int m=0;int yushu; char hex ...
- Java中将一个反斜杠转换成两个反斜杠
代码示例: s = s.replaceAll("\\\\", "\\\\\\\\");
- 56 Marvin: 一个支持GPU加速、且不依赖其他库(除cuda和cudnn)的轻量化多维深度学习(deep learning)框架介绍
0 引言 Marvin是普林斯顿视觉实验室(PrincetonVision)于2015年提出的轻量化GPU加速的多维深度学习网络框架.该框架采用纯c/c++编写,除了cuda和cudnn以外,不依赖其 ...
- Java 里把 InputStream 转换成 String 的几种方法
我们在 Java 中经常会碰到如何把 InputStream 转换成 String 的情形,比如从文件或网络得到一个 InputStream,需要转换成字符串输出或赋给别的变量. 未真正关注这个问题之 ...
- 自制 Python小工具 将markdown文件转换成Html文件
今天看到了一个Python库,名为markdown.瞬间就给了我一个灵感,那就是制作一个将markdown文件转换成html文件的小工具. 我的实验环境 操作系统: Windows 7 64位 旗舰版 ...
- 字符串数字转换成对应的Double数值
一,介绍 前面实现了字符串转换成整形数值.参考这里: 它不支持小数,不支持符号(正.负号) 现在实现一个更复杂一点字符串转换成数值的程序. 它支持“浮点字符串”转换成对应的浮点数值,如: " ...
- linux设备驱动程序-设备树(1)-dtb转换成device_node
linux设备驱动程序-设备树(1)-dtb转换成device_node 本设备树解析基于arm平台 从start_kernel开始 linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂 ...
- Java 把 InputStream 转换成 String 的几种方法
我们在 Java 中经常会碰到如何把 InputStream 转换成 String 的情形,比如从文件或网络得到一个 InputStream,需要转换成字符串输出或赋给别的变量. 未真正关注这个问题之 ...
随机推荐
- WHY翻写NEHE与红龙的3D图形程序 [开源]
个人认为学习3D图形程序开发,有两套经典教程.D3D的<D3D游戏开发编程基础>也就是红龙那本书.OpenGL的NEHE教程.为向经典致敬,将我之前翻写他们的程序开源. 我所做的工作是将其 ...
- A little tutorial on CodeFluent Entities with ASP.NET MVC4
/* Author: Jiangong SUN */ CodeFluent Entities is a model-first development tool which creates non-s ...
- 使用nginx-http-concat优化网站响应
前言: 我们在访问淘宝的时候,会看到代码中的js和css文件是通过一次请求或得的,我们知道浏览器一次请求只能并发访问数个资源,这样的处理错输在网络传输层面可以大大节省时间,这里使用的技术就是把css. ...
- Code First 中使用 ForeignKey指定外键时总是显示未引用
Code First 中使用 ForeignKey指定外键时总是显示未引用 原因是:开发环境是在.NET 4.0 修改项目,改为.net 4.5
- iOS中生成并导入基于Swift编程语言的Framework
从iOS 8.0开始就引入了framework打包方式以及Swift编程语言.我们可以主要利用Swift编程语言将自己的代码打包成framework.不过当前Xcode 7.x在自动导入framewo ...
- wdlinux 一键安装
Linux系统(支持CentOS 6.X/7.X.RedHat 6.X.Ubuntu 12.04): ssh登录服务器,执行如下操作即可,需root用户身份安装 wget http://dl.wdli ...
- memcached 源码阅读笔记
阅读 memcached 最好有 libevent 基础, memcached 是基于 libevent 构建起来的. 通由 libevent 提供的事件驱动机制触发 memcached 中的 IO ...
- SqlServer查看各个表所占空间大小的sql
CREATE TABLE [dbo].#tableinfo( 表名 [varchar](50) COLLATE Chinese_PRC_CI_AS NULL, 记录数 [int] NULL, 预留空间 ...
- LeetCode: Unique Paths II 解题报告
Unique Paths II Total Accepted: 31019 Total Submissions: 110866My Submissions Question Solution Fol ...
- Spring mvc4 + ActiveMQ 整合
一.配置部分 二.代码部分 三.页面部分 四.Controller控制器 五.效果展示 六.加入监听器 七.最最重要的,别忘了打赏 一.配置部分 ActiveMQ的安装这就不说了,很简单, 这个例子采 ...