Block探索

Block 初探

Block 是什么

首先新建main.m文件写下如下代码

1
2
3
4
5
6
int main(int argc, const char * argv[]) {
@autoreleasepool {
^{ NSLog(@"Hello, World!\n"); } ();
}
return 0;
}

通过clang -rewrite-objc main.m -o main.cpp将其转成.cpp文件,发下如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_g9_xh4404mx4pv0f7vwkvs69f_m0000gn_T_main_1beaaa_mi_0); }

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)};
  1. 一个 block 实际是一个对象,它主要由一个 impl 和 一个 descriptor 组成。
  2. impl 里面存放了 isa 指针和 FuncPtr,所有对象都有 isa 指针,用于实现对象相关的功能,FuncPtr 是函数指针,指向具体的 block 实现的函数调用地址。

所以可以得出结论,Block也可以看成是一个对象,因为他有isa指针。

Block 的类型

通常会使用到的Block类型为

_NSConcreteStackBlock   设置在栈上(编译器分配内存)
_NSConcreteGlobalBlock  设置在数据区域
_NSConcreteMallocBlock  设置在堆上(由程序员分配内存)
_NSConcreteStackBlock

局部定义的Block,示例代码。

1
2
3
4
5
int i = 0;
void (^blk)() = ^() {
printf("%d", i);
};
blk();

clang转换后

1
2
3
4
5
6
7
8
9
10
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; //设置在栈区的Block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
_NSConcreteGlobalBlock

全局定义的Block,示例代码。

1
2
3
4
5
6
7
8
9
void (^blk) () = ^{
printf("Block");
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
blk();
}
return 0;
}

clang转换后

1
2
3
4
5
6
7
8
9
10
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock; //设置在数据区的Block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

Block不需要捕获auto变量时。设置为_NSConcreteGlobalBlock

_NSConcreteMallocBlock

_NSConcreteStackBlock几乎相同。只不过用了objc_retainBlock将其从占内存copy到堆内存中。由程序员掌控生命周期。