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 函数地址,然后进行调用!
本文部分内容转自此篇华文https://www.jianshu.com/p/18f0f7fefea0
网友评论