OC底层-Block本质(一、原理)
2020/6/29 23:27:10
本文主要是介绍OC底层-Block本质(一、原理),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
问题
block的原理是怎样的?本质是什么? 复制代码
认识
block本质上是一个oc对象,内部有一个isa指针,是封装了函数调用以及函数调用环境的OC对象
探索
///简单的block代码 int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; void(^block)(int ,int) = ^(int a, int b){ NSLog(@"this is block,a = %d,b = %d",a,b); NSLog(@"this is block,age = %d",age); }; block(3,5); } return 0; } 复制代码
我们可以使用命令行将代码转化为c++查看其内部结构,与OC代码进行比较:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
分析
在C++ 代码中Block的定义如下:
void(*block)(int ,int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); 复制代码
我们可以发现block定义中调用了__main_block_impl_0结构体的初始化函数,并且将__main_block_impl_0函数的地址赋值给了block。那么我们来看一下__main_block_impl_0结构体内部的结构。如下:
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; /// 我们外层定义的int age = 10 的变量 __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; } }; 复制代码
__main_block_imp_0结构体内有一个同名构造函数__main_block_imp_0,构造函数中对一些变量进行了赋值最终会返回一个结构体。
那么也就是说最终将一个__main_block_imp_0结构体的地址赋值给了block变量
__main_block_impl_0结构体内可以发现__main_block_impl_0构造函数中传入了四个参数。(void *)__main_block_func_0、&__main_block_desc_0_DATA、age、flags。其中flage有默认值(C++语法,可以默认不传),最后的 age(_age)则表示传入的_age参数会自动赋值给age成员,相当于age = _age。
继续:函数__main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) { int age = __cself->age; // bound by copy ///我们在Block内部写的打印信息 NSLog((NSString *)&__NSConstantStringImpl__var_folders_n2_0nslhwnj04qg5hyxlg2d8ych0000gn_T_main_7998ce_mi_0,a,b); NSLog((NSString *)&__NSConstantStringImpl__var_folders_n2_0nslhwnj04qg5hyxlg2d8ych0000gn_T_main_7998ce_mi_1,age); } 复制代码
在__main_block_func_0函数中首先取出block中age的值,紧接着可以看到两个熟悉的NSLog,可以发现这两段代码恰恰是我们在block块中写下的代码。 那么__main_block_func_0函数中其实存储着我们block中写下的代码。而__main_block_impl_0函数中传入的是(void *)__main_block_func_0,也就说将我们写在block块中的代码封装成__main_block_func_0函数,并将__main_block_func_0函数的地址传入了__main_block_impl_0的构造函数中保存在结构体内。
__main_block_impl_0
结构体中有一个结构体变量和一个结构体指针源代码如下:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; ///Block块中的代码方法地址 /* 我们可以发现__block_impl结构体内部就有一个isa指针。因此可以证明block本质上就是一个oc对象。而在构造函数中将函数中传入的值分别存储在__main_block_impl_0结构体实例中,最终将结构体的地址赋值给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)}; /* 可以看到__main_block_desc_0中存储着两个参数,reserved和Block_size,并且reserved赋值为0而Block_size则存储着__main_block_impl_0的结构体占用空间大小。最终将__main_block_desc_0结构体的地址传入__main_block_func_0中赋值给Desc */ 复制代码
汇总
- __block_impl结构体中isa指针存储着&_NSConcreteStackBlock地址,可以暂时理解为其类对象地址,block就是_NSConcreteStackBlock类型的。
- block代码块中的代码被封装成__main_block_func_0函数,FuncPtr则存储着__main_block_func_0函数的地址。
- Desc指向__main_block_desc_0结构体对象,其中存储__main_block_impl_0结构体所占用的内存。
block执行内部代码
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 30, 50); 简化代码: block->FuncPtr(block, 30, 50) 复制代码
通过上述代码可以发现调用block是通过block找到FunPtr直接调用,通过上面分析我们知道block指向的是__main_block_impl_0类型结构体,但是我们发现__main_block_impl_0结构体中并不直接就可以找到FunPtr,而FunPtr是存储在__block_impl中的,为什么block可以直接调用__block_impl中的FunPtr呢?
重新查看上述源代码可以发现,(__block_impl *)block将block强制转化为__block_impl类型的,因为__block_impl是__main_block_impl_0结构体的第一个成员,相当于将__block_impl结构体的成员直接拿出来放在__main_block_impl_0中,那么也就说明__block_impl的内存地址就是__main_block_impl_0结构体的内存地址开头。所以可以转化成功。并找到FunPtr成员。
上面我们知道,FunPtr中存储着通过代码块封装的函数地址,那么调用此函数,也就是会执行代码块中的代码。并且回头查看__main_block_func_0函数,可以发现第一个参数就是__main_block_impl_0类型的指针。也就是说将block传入__main_block_func_0函数中,便于重中取出block捕获的值。
总结
此时已经基本对block的底层结构有了基本的认识,上述代码可以通过一张图展示其中各个结构体之间的关系
这篇关于OC底层-Block本质(一、原理)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值