关于__block的一些思考

参考文章

你真的理解__block修饰符的原理么?

深入研究Block捕获外部变量和__block实现原理

Block 的实现

1
2
3
4
5
6
7
8
9
10
11
// Block 源码
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^blk)(void) = ^{
printf("Block\n");
};

blk();
}
return 0;
}

使用 clang 命令将源码改成编译后的 C/C++ 语言:

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
struct __main_block_impl_0 { // block 对象
struct __block_impl impl; // 包含 block 的实现
struct __main_block_desc_0* Desc; // 包含了 block 的内存管理
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { // 构造函数
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp; // block 中的代码,包含 __main_block_func_0 方法
Desc = desc;
}
};
// block 中的代码,传入 block 以便获取其中的捕获的值或变量
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

printf("Block\n");
}

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 argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// 创建一个 block,调用构造方法
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// 调用 block,即调用 __main_block_func_0 方法
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}

关系图:
image

流程:定义 block 的时候,调用 main_block_impl_0 的构造函数,创建 block,main_block_impl_0 结构体中包括 block_impl(block 的实现,而这个结构体又包含 block 中的代码,及静态方法 main_block_func_0),并且把捕获进来的变量成为 __main_block_impl_0 的成员变量。

调用 block 的时候,即调用 block_impl 结构体中的 main_block_func_0 方法,并且把 __block_impl 自身传入方法,以供方法取得值或变量。

为什么 __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
38
39
40
41
42
43
44
45
46
47
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)++;
printf("%d\n",(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(int argc, const char * argv[]) {
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 10};
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

(i.__forwarding->i)++;

((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}

block 拷贝到堆上,捕获的自动变量值也拷贝到了堆上。
image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int i = 10;
void (^blk)(void) = ^{
i++;
printf("%d\n",i);
};

i++;

blk();
}
return 0;
}
// 输出
12

由于Block_byref_i_0 中有一个可以指向本类型的 forwarding 指针。

在调用 block 之前,即未拷贝之前,栈上的 __forwarding 指向自己,进行一次自增。

经过拷贝之后,栈上的 forwarding 指针指向堆上的变量,而堆上的 forwarding 指针又指向自己。

这样不论是堆中的变量还是栈中的变量调用 (i->forwarding->i)++; ,都是堆上变量的值发生了改变,而栈中变量的值则指向了堆中变量的值,即对 block 中的变量进行自增操作,栈中的值也发生了改变(forwarding 指针改变了指向,指向堆中的变量)即对外部的自动变量也可以进行自增操作。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2019 Acan's blog All Rights Reserved.

访客数 : | 访问量 :