block 底层分析

文章目录
  1. 1. block 源码分析
  2. 2. __block
  3. 3. block 的类型

为什么oc 会设计block 呢?
oc 是 c扩展,c 中第一个想到的事回调函数—— 用来做控制翻转
可是 callback 不能捕获变量

而 block 本质 objc_object 类型,它可以捕获变量,更好地做到控制翻转

block 会将捕获的变量封装称自己的成员变量

捕获变量的类型

  1. auto:copy,所以block跟外部的值不一样
  2. static:指针引用
  3. 全局变量:不捕获,直接访问
  4. pointer:指针引用

block 源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;// block 方法-- __test_block_func_1
};

struct __test_block_impl_1 {
struct __block_impl impl;
struct __test_block_desc_1* Desc;
int age;
__test_block_impl_1(void *fp, struct __test_block_desc_1 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

static void __test_block_func_1(struct __test_block_impl_1 *__cself) {
int age = __cself->age; // bound by copy
NSLog("block:%d", age);
}

static struct __test_block_desc_1 {
size_t reserved;
size_t Block_size;// block结构体大小
} __test_block_desc_1_DATA = { 0, sizeof(struct __test_block_impl_1)};


void test(){
// Stack:访问了auto变量
int age = 10;
void (^block)(void) = ^{
NSLog(@"block:%d", age);
};
}

__block

1
2
3
4
5
6
7
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};

__block int age = 10;
此时 age 就已经被编译成一个 oc对象了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;//这个 age 是__Block_byref_age_0结构体

// 下面的 age 都是临时变量,__Block_byref_age_0 的age 属性
printf("1:%p\n", &age);
Block block = ^{
printf("2:%p\n", &age);
};
block();
printf("3:%p\n", &age);
}
return 0;
}
//output
/*
1:0x7ffeefbff458
2:0x100719078
3:0x100719078
*/

将变量封装了一个 objc_object 类型,这样对于 auto 变量使用 __block 修饰,就可以跟外面一致了

block 的类型

  • __NSGlobalBlock__ ( _NSConcreteGlobalBlock )
  • __NSStackBlock__ ( _NSConcreteStackBlock )
  • __NSMallocBlock__ ( _NSConcreteMallocBlock )