block是封装了函数调用以及函数调用环境的OC对象
先看一个block捕获外部auto变量age,通过命令clang xxx.m -rewrite-objc -o xxx.cpp或者xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx.m -o xxx.cpp将.m文件转成c++代码
OC代码 C++代码block就是指向__main_block_impl_0结构体指针,从上面可以看出是调用了__main_block_impl_0方法并传了三个参数__main_block_func_0(block内部执行的代码块也就是函数的实现)、__main_block_desc_0_DATA(block大小信息)和外部auto变量age,这里的age是值传递,再看下__main_block_impl_0这个方法和其他的两个参数
__main_block_impl_0方法定义在结构体__main_block_impl_0中,c++语法,这是结构体__main_block_impl_0的构造方法,默认返回一个__main_block_impl_0结构体,和OC的init有点类似
__main_block_func_0是block内部需要执行的代码块
__main_block_desc_0_DATA存放着block所占的内存大小(Block_size),reserved暂时没有作用
再看下__main_block_impl_0结构体中的第一个成员,第一个成员是结构体,也就相当于直接将__main_block_impl_0中的内容拷贝到__main_block_impl_0中
可以看到里面有个isa指针,这也就说明了block是OC对象(还可以通过打印[[[[myBlock class] superclass] superclass] superclass]来查看block最终是继承自NSObject),还有一个FuncPtr指针,该指针指向了block内部需要执行的代码块地址,再看调用代码
((void(*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
直接将结构体__main_block_impl_0 block强转成__block_impl结构体取出block内部的代码块地址进行调用,这里可以强转的原因是由于__block_impl是__main_block_impl_0的第一个成员,由于结构体的第一个成员地址就是结构体的地址,所以可以直接强转,调用时取出从外部捕获的变量age(block在堆区,而且block内部的age是const只读的),然后打印。
再看下block捕获外部static变量,编译成c++代码
可以看出和上面捕获的auto变量有些差别,auto变量被捕获时直接传入的age的值,而static变量被捕获时传入的是age的地址(&age),由于auto变量出了当前作用域内存就会被销毁,所以需要将auto变量的值捕获住,static变量是一直存在内存中,但出了作用域就访问不了啦,所以只需要捕获static变量的内存地址就可以了
再看下全局变量
可以看出全局变量是不会被捕获的,因为全局变量在哪里都可以访问,不需要进行捕获
所以得出的结论是:局部变量是一定会被捕获,全局变量不会被捕获;
局部变量的捕获方式:auto变量是值捕获,static是地址捕获
网友评论