首先来看看block截获自动变量(外部局部变量)的几种情况
block“带有自动变量(截获局部变量)”的含义在于具有“截获自动变量值”的能力
ARC中常见的block用法场景如下:
由以上结果总结如下:
- 默认block的类型是NSGlobalBlock,一旦在block中引用自动变量(包括OC对象),无论是否修改,block类型变为NSMallocBlock
- 默认情况下,在block内不能对截获的自动变量(或OC对象)进行修改,编译器会报错
- 默认情况下,在block中引用的自动变量或OC对象(包括调用OC对象方法),block中的自动变量地址不同,而引用前后自动变量的地址一致
- 在自动变量(或OC对象)前添加__block,无论是否修改自动变动,引用后与block自动变量的地址一致,而且与引用前不一致
另外,需要注意的一点是,block目前没有实现对C语言数组的截获,例如以下代码会产生编译错误
使用指针可以解决这个问题
- (void)blockCaptureCArray {
// C 语言的数组
const char *text = "hello";
void (^blockTest) (void) = ^{
NSLog(@"%c",text[0]);
};
blockTest();
}
block的实质
可以通过clang -rewrite-objc 源代码文件名
将含有block的代码变换为C++的源代码进行分析,将如下main.m文件
#import <stdio.h>
int main() {
void (^blk) (void) = ^{
printf("Block\n");
};
blk();
return 0;
}
打开终端,在main.m所在目录下键入clang -rewrite-objc main.m即可在当前目录下生成一个main.cpp文件,主要分为:
1、__block_impl结构体定义
// 结构体 __block_impl 的声明
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
2、__main_block_impl_0结构体定义,其中包含其构造函数
参数fp传入__main_block_func_0函数的地址,赋值给__block_impl类型的结构体实例impl的函数指针FuncPtr
参数desc传入的&__main_block_desc_0_DATA结构体取地址赋值给了__main_block_desc_0类型的结构体指针Desc
__block_impl类型的结构体实例impl的isa指针存放了_NSConcreteStackBlock类的地址,旨在将block作为OC的对象处理,将block类的信息放置于_NSConcreteStackBlock,由此可见,block的实质是OC对象
// 结构体 __main_block_impl_0 的声明
struct __main_block_impl_0 {
// 成员变量 impl
struct __block_impl impl;
// 指针
struct __main_block_desc_0* Desc;
// 结构体 __main_block_impl_0 的构造函数
__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;
}
};
3、block函数转化后的源代码
// __cself 是 __main_block_impl_0 结构体的指针
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
// block的描述
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)
};
4、程序执行的main函数
/* main.m 中 main 函数转换后的源代码*/
int main() {
// 左边是一个无参无返回值的函数指针
// 右边__main_block_impl_0结构体包含自己的属性、构造方法、成员方法,然后取其地址
void (*blk) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// 代码转换1
struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
// 对应代码 block();
// 前半部分访问结构体 __block_impl 自己的函数指针 FuncPtr,并且将 block 自身作为参数传递
// 代码转换2
(*blk->impl.FuncPtr)(blk);
return 0;
}
又见block(二):block语法定义
又见block(四):block捕获自动变量
又见block(五): __block变量和对象
又见block(六):block存储域与__block变量存储域
又见block(七):截获对象