「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」。
OC基础- block
Block:本质:一个OC对象,一个封装了函数调用以及函数调用环境的OC对象
先定义一个简单的block,上代码
Int age = 23;
void (^newBlock)(int a, int b) = ^(int a,int b){
NSLog(@"测试Block1 a = %d",age);
NSLog(@"测试Block2 b = %d",b);
};
将其转化成.cpp文件以便查看源码
xcrun -sdk iphones clang -arch arm64 -rewrite-objc main.m
我们得到刚才那一段代码的本质
int age = 10;
//定义block对象
void (*newBlock)(int a, int b) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
//执行block内部代码
((void (*)(__block_impl *, int, int))((__block_impl *)newBlock)->FuncPtr)((__block_impl *)newBlock, 10, 30);
经过筛选得出来
block组成部分
struct __main_block_impl_0 {
struct __block_impl impl;
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0* Desc;
int age; //=>访问外部的age
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
其中__block_impl结构如下所示
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
其中struct __main_block_desc_0 里面结构如下(描述信息)
struct __main_block_desc0{
size_t reserved;
size_t Block_size;
};
从main函数可以看出来定义block的时候是直接把age值传进去的。
综合,
1.为什么说block是一个OC对象,因为内部有isa指针
2.为什么说block一个封装了函数调用以及函数调用环境的OC对象
看下图
block内部只有两个参数,一个impl,一个Desc,而函数的调用地址 - FuncPtr是再impl中的,为什么这里能直接这样写呢?
因为,__main_block_impl_0 结构的地址和他的第一个成员一样,第一个成员的地址是__block_impl,所以__main_block_impl_0 和 __block_impl 的地址其实是同一个,通过格式强制转换,将 main_block_impl_0 转成 block_impl 就可以直接拿到他内部的 FuncPtr 函数地址,然后进行调用!
OC基础-Block(2)Block的变量捕获
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
Block捕获机制
接下来一一验证
auto类型是一种默认类型
auto类型局部变量(值传递)(原因,auto类型会随时销毁,所以趁它有值的时候就获取进来)
int age = 10; 相当于 auto int age = 10;
void (^newBlock)(void) = ^(void){
NSLog(@"测试Block1 age = %d",age);
};
age = 30;
newBlock();
打印结果
2022-02-23 10:32:54.982175+0800 DoBlock[69542:1863431] 测试Block1 age = 10
查看源码
···
int age = 10;
void (*newBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
age = 30;
((void (*)(__block_impl *))((__block_impl *)newBlock)->FuncPtr)((__block_impl *)newBlock);
···