Aspects源码解读:动态Block调用(不定参数的Block)
2015-10-13 17:53
911 查看
Aspects是iOS开发中比较著名的AOP开源库,体积小巧而功能强大,其使用的时候有个地方比较有意思:
对于usingBlock这个参数,其传递的block是根据不同的hook方法而可以不同的,比如下面这三个hook调用,其usingBlock就可以带不同个数的参数:
那么Aspects是如何做到这一点的呢?
打开其源码,一路跟着参数block走下去,我们可以看到,block参数在进入方法
后被进行了转换处理,通过block参数生成了一个NSMethodSignature,这货相信对runtime比较熟悉的人还是知道是什么的。那么这里的原理又是什么呢?
我们都知道Block其实是一个对象,但是这个对象究竟是啥呢?得益于runtime的开源,我们可以知道其实他是一个结构体,而Aspects文件也定义了一个叫AspectBlockRef的结构体,源码入下:
通过对比,这个是Block的结构体形式相同,可以用来解析block的内部数据。从而生成相应的NSMethodSignature。
后面的事情说起来就比较简单了,对于用于触发block的调用方法:
其内部做了几件事:
通过block的NSMethodSignature生成对应的NSInvocation
把待替换原始方法的参数,赋值给block的invocation
通过invocation触发block的调用
为了直观的解释,我将Aspects的该方法关键点注释出来:
总结下来,要实现这样的可变个数参数的block调用,关键还是在于获得其NSInvocation。
最后,推荐另一篇block调用相关的文章:《NSInvocation动态调用任意block》,其原理上与本文解析相似。
[code]+ (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;
对于usingBlock这个参数,其传递的block是根据不同的hook方法而可以不同的,比如下面这三个hook调用,其usingBlock就可以带不同个数的参数:
[code][UIViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionAfter usingBlock:^(){ }]; withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info){ }]; [UIView aspect_hookSelector:@selector(initWithFrame:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, CGRect frame){ }];
那么Aspects是如何做到这一点的呢?
打开其源码,一路跟着参数block走下去,我们可以看到,block参数在进入方法
[code]static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error)
后被进行了转换处理,通过block参数生成了一个NSMethodSignature,这货相信对runtime比较熟悉的人还是知道是什么的。那么这里的原理又是什么呢?
我们都知道Block其实是一个对象,但是这个对象究竟是啥呢?得益于runtime的开源,我们可以知道其实他是一个结构体,而Aspects文件也定义了一个叫AspectBlockRef的结构体,源码入下:
[code]typedef struct _AspectBlock { __unused Class isa; AspectBlockFlags flags; __unused int reserved; void (__unused *invoke)(struct _AspectBlock *block, ...); struct { unsigned long int reserved; unsigned long int size; // requires AspectBlockFlagsHasCopyDisposeHelpers void (*copy)(void *dst, const void *src); void (*dispose)(const void *); // requires AspectBlockFlagsHasSignature const char *signature; const char *layout; } *descriptor; // imported variables } *AspectBlockRef;
通过对比,这个是Block的结构体形式相同,可以用来解析block的内部数据。从而生成相应的NSMethodSignature。
后面的事情说起来就比较简单了,对于用于触发block的调用方法:
[code]- (BOOL)invokeWithInfo:(id<AspectInfo>)info
其内部做了几件事:
通过block的NSMethodSignature生成对应的NSInvocation
把待替换原始方法的参数,赋值给block的invocation
通过invocation触发block的调用
为了直观的解释,我将Aspects的该方法关键点注释出来:
[code]- (BOOL)invokeWithInfo:(id<AspectInfo>)info { NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature]; NSInvocation *originalInvocation = info.originalInvocation; NSUInteger numberOfArguments = self.blockSignature.numberOfArguments; // 检查block中写了几个参数,如果比原方法个数还多,那么是有问题的 if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) { AspectLogError(@"Block has too many arguments. Not calling %@", info); return NO; } // 如果block中有参数,那么先把info放到1位置上 if (numberOfArguments > 1) { [blockInvocation setArgument:&info atIndex:1]; } //根据block中的参数个数,从原方法中逐个赋值过来 void *argBuf = NULL; for (NSUInteger idx = 2; idx < numberOfArguments; idx++) { const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx]; NSUInteger argSize; NSGetSizeAndAlignment(type, &argSize, NULL); if (!(argBuf = reallocf(argBuf, argSize))) {//分配内存 AspectLogError(@"Failed to allocate memory for block invocation."); return NO; } //获取原方法中的参数 [originalInvocation getArgument:argBuf atIndex:idx]; //把参数赋值给block调用 [blockInvocation setArgument:argBuf atIndex:idx]; } //触发block方法调用 [blockInvocation invokeWithTarget:self.block]; if (argBuf != NULL) { free(argBuf); } return YES; }
总结下来,要实现这样的可变个数参数的block调用,关键还是在于获得其NSInvocation。
最后,推荐另一篇block调用相关的文章:《NSInvocation动态调用任意block》,其原理上与本文解析相似。
相关文章推荐
- ASP学习笔记
- ASP.NET WEBApi
- asp.net ado.net setParameter
- 认识Asp.net 中相对路径与绝对路径
- 最简实例演示asp.net5中用户认证和授权(3)
- JasperReport表头的stretchType属性无效的解决
- ASP.NET的实用技巧详细介绍
- ASP.NET MVC5 网站开发实践
- ASP.NET MVC 分部页 PartialViewResult
- asp.net5中用户认证与授权(2)
- asp.net5中的用户认证与授权(1)
- 最简实例演示asp.net5中用户认证和授权(2)
- ASP.NET MVC下的异步Action的定义和执行原理
- ASP.NET动态网站制作(11)-- JQ(3)
- 最简实例演示asp.net5中用户认证和授权(1)
- ASP.NET MVC的四种验证编程方式
- ASP.Net MVC开发基础学习笔记(1):走向MVC模式
- ASP.Net MVC 给母版页视图传递数据
- 图文详解远程部署ASP.NET MVC 5项目 [转载]
- ASP.NET书籍信息录入实现代码