NSInvocation动态调用任意block
2016-06-03 07:31
761 查看
http://www.wtoutiao.com/p/i7bspr.html
我们知道常规的block调用都是硬编码写死,参数类型必须在编译时匹配,编译器会转换成struct与C函数指针,比如下代码:
会被编译为:
假设我写这样的代码,则会出现编译错误:
必须做强制转换才能编译通过:
而JavascriptCore可以很简单的设置任意数据类型的block, 让js调用. 比如:
那么block是怎么被动态调用的呢?
我们打个断点,可以看到如图堆栈:
你会看到竟然出现了-[NSInvocation invoke];也就是说, block是被NSInvocation调用的!
用过NSInvocation的应该知道, 它可以用来实现对指定对象的任意方法的调用, 类似如下常用方式:
可是对于block来说,它的selector是什么?如何设置它的selector?
我们把JavascriptCore源码拿下来, 看看究竟做了什么?
找到这两个函数,你是否猜到了调用方式?
并没有selector, 调用逻辑是:
获取block的signature->设置target为block->设置参数->invoke
我们按照这个思路写一段代码试试:
确实完成了block的调用!尝试成功了!
那么_Block_signature又是什么?
在JavascriptCore的ObjcRuntimeExtras.h文件中找到了如下代码:
原来_Block_has_signature是iOS的私有API,用来获取block的参数特征,可以看到JavascriptCore还用到了其他几个私有API.
如果我想用_Block_has_signature又担心被AppStore拒,怎么办?
淡定,既然block是一个struct,而objective-c runtime又是开源的,那么必然可以构造相同的struct然后解析里面的数据, 当然万能的github已经有CTObjectiveCRuntimeAdditions库提供此功能了.
使用很简单,把invocation的初始化改成如下代码即可.
顺便我们可以看下block的MethodSignature是什么样的:
可以看到参数的描述非常详细包含了参数个数、参数所占字节大小、参数内存地址偏移、参数类型标识等。
argument 0的符号是”@?”代表block, argument 1不是selector而是第一个参数, 所以setArgument是从1开始, 而不是2.
有了NSInvocation调用block,我们就可以实现在lua、js等脚本语言中调用获取到的任意OC block了!大致流程如下:
首先我们编写一个能调用任意block的函数:
然后我们将一个名为invokeBlock的函数注册给脚本语言,由他去接收和组装参数。
接着由invokeBlock将组装好的参数传给testInvokeAnyBlock并调用。
那么脚本中就可以实现block的调用了,类似如下代码:
至此我们通过断点的追踪、源码的分析、代码的尝试,弄清楚并实现通过NSInvocation动态调用任意block,当你需要在某些场景中动态调用OC的block时, 就可以派上用场了~
我们知道常规的block调用都是硬编码写死,参数类型必须在编译时匹配,编译器会转换成struct与C函数指针,比如下代码:
会被编译为:
假设我写这样的代码,则会出现编译错误:
id anyBlock = sumBlock; anyBlock(1, 2, 3);
必须做强制转换才能编译通过:
id anyBlock = sumBlock; ((int (*)(int, int, int))anyBlock)(1, 2, 3);
而JavascriptCore可以很简单的设置任意数据类型的block, 让js调用. 比如:
那么block是怎么被动态调用的呢?
我们打个断点,可以看到如图堆栈:
你会看到竟然出现了-[NSInvocation invoke];也就是说, block是被NSInvocation调用的!
用过NSInvocation的应该知道, 它可以用来实现对指定对象的任意方法的调用, 类似如下常用方式:
可是对于block来说,它的selector是什么?如何设置它的selector?
我们把JavascriptCore源码拿下来, 看看究竟做了什么?
找到这两个函数,你是否猜到了调用方式?
并没有selector, 调用逻辑是:
获取block的signature->设置target为block->设置参数->invoke
我们按照这个思路写一段代码试试:
确实完成了block的调用!尝试成功了!
那么_Block_signature又是什么?
在JavascriptCore的ObjcRuntimeExtras.h文件中找到了如下代码:
原来_Block_has_signature是iOS的私有API,用来获取block的参数特征,可以看到JavascriptCore还用到了其他几个私有API.
如果我想用_Block_has_signature又担心被AppStore拒,怎么办?
淡定,既然block是一个struct,而objective-c runtime又是开源的,那么必然可以构造相同的struct然后解析里面的数据, 当然万能的github已经有CTObjectiveCRuntimeAdditions库提供此功能了.
使用很简单,把invocation的初始化改成如下代码即可.
顺便我们可以看下block的MethodSignature是什么样的:
可以看到参数的描述非常详细包含了参数个数、参数所占字节大小、参数内存地址偏移、参数类型标识等。
argument 0的符号是”@?”代表block, argument 1不是selector而是第一个参数, 所以setArgument是从1开始, 而不是2.
有了NSInvocation调用block,我们就可以实现在lua、js等脚本语言中调用获取到的任意OC block了!大致流程如下:
首先我们编写一个能调用任意block的函数:
然后我们将一个名为invokeBlock的函数注册给脚本语言,由他去接收和组装参数。
接着由invokeBlock将组装好的参数传给testInvokeAnyBlock并调用。
那么脚本中就可以实现block的调用了,类似如下代码:
至此我们通过断点的追踪、源码的分析、代码的尝试,弄清楚并实现通过NSInvocation动态调用任意block,当你需要在某些场景中动态调用OC的block时, 就可以派上用场了~
相关文章推荐
- 惰性函数
- 任意网页资源随手拈来
- 【面试】【Struts2常见问题总结】【02】
- OpenGL显示列表
- ReactJS简介
- Tile-Based Deferred Rendering
- 文件上传-多个文件
- 文件上传-单个文件
- Linux远程登录
- 细说ASP.NET Core与OWIN的关系
- 微软发布正式版SQL Server 2016
- 团队第二次冲刺10
- 汇编指令
- 游戏粉丝的福音
- mybatis 批量插入 list<object> in oracle
- ubuntu解压rar格式文件
- 购买图书
- [leetcode] 141. Linked List Cycle
- CC2530之DHT11温湿度测量
- 猎头顾问那些年,那些奇葩候选人——北漂18年(58)