Listed


关于__block的实现

我们都知道, 如果要在block中修改外部的变量, 要使用__block来修饰, 那么为什么这么做呢, 里面的原理是什么?

经过实践查阅源码后,我们可以了解到:

block底层是一个struct, block的参数对应着struct的构造函数参数, block代码块对应着一个函数. 这个函数的参数是当前block. 不加__block,外部变量copy到block内部,底层是通过struct构造函数将值传递进去,因为是值传递,所以修改不了外面的值. 加了__block,外面的变量的引用,通过struct的构造函数传递进去,内部持有了这个引用,是个引用传递,所以可以修改外面的值.

测试的原始block

1
2
3
4
5
// __block NSInteger num = 10;
NSInteger num = 10;	
void (^add)(void) = ^void(void) {
    NSLog(@"%ld", num);
};

不加__block, clang rewrite之后的源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSInteger num;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger _num, int flags=0) : num(_num) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  NSInteger num = __cself->num; // bound by copy
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_bm_939gjfvj42b5bzxh93zwnxcm0000gn_T_main_db5d50_mi_0, num);
}

加了__blcok, clang rewrite之后的源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct __Block_byref_num_0 {
  void *__isa;
  __Block_byref_num_0 *__forwarding;
  int __flags;
  int __size;
  NSInteger num;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_num_0 *num; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_num_0 *_num, int flags=0) : num(_num->__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_num_0 *num = __cself->num; // bound by ref
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_bm_939gjfvj42b5bzxh93zwnxcm0000gn_T_main_3ba1e4_mi_0, (num->__forwarding->num));
}