Skip to content

Latest commit

 

History

History
123 lines (91 loc) · 3.91 KB

Object-C Block源码分析.md

File metadata and controls

123 lines (91 loc) · 3.91 KB

首先我们看一个最简单的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的运行逻辑了。

那么为什么会需要一个forwarding指针指向自身呢?看下面这个例子:

int main(int argc, const char * argv[]) {
    __block int val = 0;
    void (^blk)(void) = [^{ ++val; } copy];
    ++val;
    blk();
    printf("val = %d", val);
    return 0;
}

我们有一个__block修饰的变量val,那么根据blk中我们知道,这个block会被复制到堆上,而对应的val生成的结构__Block_byref_val_0的值也会被复制到堆上去,那么在栈上的val该怎么跟堆上的做到统一呢?那么这个时候我们就设计了,将栈上的val结构体的forwarding指针的值指向栈上的该对象。通过编译后的代码,我们也能看出,如下:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref
 ++(val->__forwarding->val); }

++(val.__forwarding->val);