block的底层实现

最近比较忙,微信的暂时就没有更新了。好多人都在问我,block是怎么一回事情,我们怎么通过__block就可以修改外部变量的值呢?等等,好吧,那我就将我的研究成果简单的写一篇博客吧,同时感谢来自这里写链接内容的分享。

无参数的block

大家还记得无参block的申明和实现吧:

#include <stdio.h>
int main() {
    void (^block)(void) = ^{ printf("asdn"); };
    block();
    return 0;
}

其实这已经是很上层的东西了,我们来看看他的中间代码。通过Clang可以查看他的中间代码:
block的底层实现_第1张图片
这样后,我们会有一个test.cpp的文件。打开它你会看到有这些东西:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

.
.
.

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 printf("asdn"); }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main() {
 void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
 return 0;
}

shit,一堆东西,看不懂对不对?
我们来简单的介绍一下这个东西,第一个结构体也就是该block的结构体。我们可以看到他有一个isa指针,它表明了这个block可以是任意数据类型的。然后注意到一个FuncPtr的函数指针,它指向了该block的函数实现地址。似乎明了了吧。然后我们来看看block是如何实现的。当我们申明了一个block:

void (^block)(void) = ^{ printf("asdn"); };

实际上在main函数中会转变成这样的代码:

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

what the fuck!又看不懂了。=号前面的我们不管,直接看

&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))

这里实际上使用了

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

结构体的一个构造函数。在这个结构体中还有两个成员变量,一个是block的结构体,另外一个是该结构体的描述信息。
而描述信息也是一个结构体,其中Block_size相对重要,它标志了__main_block_impl_0结构体的空间大小。
这时候我们注意到一个函数

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 printf("asdn"); }

明白人一看就知道这个是该block的函数实现。注意一下这里有一个参数__cself,实际上就是block本身。
最后我们看block是如何调用的

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

可以看出,它就是调用了block结构体的FuncPtr函数,并将自己block作为参数传入进去。

有参数的block

我们将代码更改成:

#include <stdio.h>
int main() {
    void (^block)(int) = ^(int i){ printf("%d",i); };
    block(5);
    return 0;
}

再获得中间代码后:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int i) {
 printf("%d",i); }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main() {
 void (*block)(int) = ((void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
 ((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 5);
 return 0;
}

注意到所有的地方都没有改变,只是在函数的实现中增加了一个参数。是不是言简意赅?

block改变外部变量

首先我们来看它是如何访问外部变量的:

#include <stdio.h>
int main() {
    int i = 5;
    void (^block)(void) = ^{ printf("%d",i); };
    block();
    return 0;
}

查看源码后会发现有这样的不同:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int i = __cself->i; // bound by copy
 printf("%d",i); }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main() {
    int i = 5;
 void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
 return 0;
}

在__main_block_impl_0结构体中多出了一个int i;实际上他就是我们的外部变量的内容。在函数实现里面,我们会先将这个i也就是__cself->i赋给函数内的局部变量int i,然后打印i的值。
其次我们再来看看我们是如何改变外部变量的值,中所周知,我们会加上__block来修饰外部变量i

#include <stdio.h>
int main() {
    __block int i = 5;
    void (^block)(void) = ^{ i = 8;printf("%d",i); };
    block();
    return 0;
}

这时候我们会发现中间代码是这样的:

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int i;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_i_0 *i = __cself->i; // bound by ref
 (i->__forwarding->i) = 8;printf("%d",(i->__forwarding->i)); }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main() {
    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 5};
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
 return 0;
}

这时候几乎是大变样了。但是我们只观察重点,我们会发现事实上__main_block_impl_0结构体重存了一个__Block_byref_i_0 *i;而__Block_byref_i_0结构体中有两个比较重要的内容,一个是int i,存放外部变量i的内容,而__forwarding结构体指针变量则是指向自己的。我们通过__forwarding访问i来改变i的值就可以实现改变了:

__Block_byref_i_0 *i = __cself->i; // bound by ref
 (i->__forwarding->i) = 8;

block改变静态变量

#include <stdio.h>
int main() {
    static int i = 5;
    void (^block)(void) = ^{ i = 8;printf("%d",i); };
    block();
    return 0;
}

中间代码如下:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *i;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *i = __cself->i; // bound by copy
 (*i) = 8;printf("%d",(*i)); }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main() {
    static int i = 5;
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i));
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
 return 0;
}

相信不用我再多说什么了吧,你可以知道静态变量的话其实就是根据指针来修改内容了吧。

总结

通过上面的演示,不知道各位对block是否有了一定的深入了解呢?很多时候,我们可以通过中间代码来探究原理性的知识。

你可能感兴趣的:(block)