iOS底层:程序Block原理

  • Block原理

从源码说起

int main(int argc, char * argv[]) {
    @autoreleasepool {

        int a = 0;
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };

       block();

        return 0;
    }
}

翻译成底层C++之后

int a = 10;

void (*block)(void) = ((void (*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, a));

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl*)block);

分解第一步:__main_block_impl_0结构体

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

这里调用了他的构造函数,

第一个参数__main_block_func_0会创建一个__main_block_func_0静态函数,这个函数中会生成block中会使用到的block变量,然后将这个静态函数保存到了__block_impl

第三个参数会从block外将需要的变量赋值到block块的结构体内。

因此:

1. block外部的变量a在后续无论做什么操作,都不会影响block内部保存的变量a
2. 这就是在block内部直接修改自动变量会报错的原因,
3. 如果这里直接允许修改了,在目前条件下,也仅仅能做到block内部的变量a进行重新赋值操作,和外部变量a没有关系,产生歧义。

分解第二步:调用结构体

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl*)block);

block指针指向的是__main_block_impl_0 的首地址,即__block_impl的地址,所以可以强转为(__block_impl *)类型,并访问其成员FuncPtr,指向的是静态函数地址,并传入参数__main_block_impl_0,也就是block自己。

总结:

名称 类型 是否随block内容改变 生成顺序 说明
__block_impl 结构体 NO 1 底层结构体,属于__main_block_impl_0成员
__main_block_impl_0 结构体 YES 2 缓存变量/对象,主结构体
__main_block_func_0 静态函数 YES 2 缓存代码,地址存放在__block_impl中

将外部变量/对象的信息缓存在__main_block_impl_0中,
将代码缓存在静态函数中,
静态函数在缓存代码的时候需要用到外部变量/对象的信息
执行block就是执行了该静态函数

猜你喜欢

转载自blog.csdn.net/odyyy/article/details/87967842