NSInvocation动态调用任意block

http://www.wtoutiao.com/p/i7bspr.html

我们知道常规的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时, 就可以派上用场了~


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值