最近在项目中遇到了一个问题,如何动态创建一个block呢?提到动态创建,大家可能更多想到的是OC方法,但是block能否能像OC方法一样动态添加呢?为了解决这个问题我们首先要了解OC的block到底是什么,只有弄清楚什么block的结构才能谈及动态创建。
block的结构
block是对象,OC中对象的本质上是结构体,因此block实际上是一个结构体,在Clang 7 documentation中有对block的详细描述:
先来看几个比较重要的内容:
首先是void (*invoke)(void *, ...),这是一个函数指针,它是block所执行代码的地址,也就是说block之所以能像函数一样执行都是因为有了这个函数指针,它的第一个参数是固定的,从第二个参数位置起才是block真正的参数。这一点很像OC的方法,OC的方法从第3个参数位置起才是真正的参数,前两位分别是self和cmd。而block的第一个参数猜测很可能是block自己的地址。
void (*copy_helper)(void *dst, void *src); 是blcoK在拷贝时的回调,也就是说block在执行拷贝操作时我们可以拿到拷贝前和拷贝后的block。
void (*dispose_helper)(void *src);是block在将被销毁时的回调。
const char *signature;是block的签名,这个签名类似OC方法的签名,它描述了block的返回值、参数信息。
如何生成block?
因为block本身就是个结构体,那么是否意味我们创建一个与block结构一样的结构体,然后对这个结构体相应进行赋值是不是就可以达到创建block的目的了呢?实际上JSPatch就是这样创建block的。JSPatch的关键方法如下:
JSPatch主要通过以上6步骤实现了动态生成block。我们在生成block时的思路与此类似,但是由于之前没有注意到void (*dispose_helper)(void *src);的用途,采用了另一种方式来管理内存。虽然最终纠正过来了,但是还是值得一提的:当我们将生成的block返回给外界时,实际上block的生命周期已经不归我们管理了,如果拿不到block的释放时机(实际上是可以拿到的)的话,那么怎么感知到block被释放了呢?在这里我们借助了关联引用
首先创建了一个自定义类PhoiexFlagObject,并在PhoiexFlagObject的delloc方法中实现相关内存清理工作,此时如果外界的block释放时会对对象flagObject做一次release操作,flagObject触发delloc,这样我们就能拿到返回给外界的block释放时的时机了。文章相关libffi内容可以参考jiayoubaobao的相关文章。