Block的实现逻辑浅析

412 阅读1分钟

首先我们看一个最简单的Block的实现代码:

void blockMain() {
    void(^MyBlock)(void) =  ^{
            printf("block test");
        };
    MyBlock();
}

通过clang -rewrite-objc block.c后会得到一个block.cpp的文件,里面就是转换后的代码:

struct __blockMain_block_impl_0 {
  struct __block_impl impl;
  struct __blockMain_block_desc_0* Desc;
  __blockMain_block_impl_0(void *fp, struct __blockMain_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

我们可以看到,转换后的block __blockMain_block_impl_0 = 函数名 + block_impl + block的顺序,里面有两个变量,变量的类型为两个结构体,如下:

struct __block_impl {
  void *isa; // block指向block所属的类型(_NSConcreteGlobalBlock, _NSConcreteStackBlock, _NSConcreteMallocBlock)
  int Flags; 
  int Reserved;
  void *FuncPtr; // 回调的函数指针
};

static struct __blockMain_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blockMain_block_desc_0_DATA = { 0, sizeof(struct __blockMain_block_impl_0)};

最后看blockMain:

static void __blockMain_block_func_0(struct __blockMain_block_impl_0 *__cself) {

        printf("block test");
    }

void blockMain() {

    void(*MyBlock)(void) = ((void (*)())&__blockMain_block_impl_0((void *)__blockMain_block_func_0, &__blockMain_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);

}

Block带参数

struct __blockMain_block_impl_0 {
  struct __block_impl impl;
  struct __blockMain_block_desc_0* Desc;
  int a;
  int b;
  __blockMain_block_impl_0(void *fp, struct __blockMain_block_desc_0 *desc, int _a, int _b, int flags=0) : a(_a), b(_b) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __blockMain_block_func_0(struct __blockMain_block_impl_0 *__cself) {
// __cself指向block的指针
  int a = __cself->a; // bound by copy
  int b = __cself->b; // bound by copy

        int c = a + b;
        printf("%d", c);
    }
    

首先,C++中,函数名后面带上冒号是它特有的赋值方式,所以,: a(_a), b(_b)表示给a与b赋值。与不带参数的block相比,带参数的block其实也就是多了几个参数,即对参数进行了复制。

__block修饰符

添加__block修饰符的变量,在编译后会多出:

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

结构,其中__forwarding指向自身。因此在block内部就是通过这个指针,来修改block外部的变量。

如下:

static void __blockMain_block_func_0(struct __blockMain_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref

        (a->__forwarding->a) = 2;
        printf("%d", (a->__forwarding->a));
    }
    

所以看完编译后的代码,我们就能够知道block的运行逻辑了。