ObjC block简析(一)

栏目: Objective-C · 发布时间: 5年前

内容简介:在main.m的main函数中声明一个block并执行在main.cpp中可以发现上图中的代码。其中在main函数中的block和block(),被转化为其中不乏强制转换类型的代码,将强制类型转换的代码去掉我们可以明确的看出:
ObjC block简析(一)

在main.m的main函数中声明一个block并执行 block() 通过 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp 命令将main.m转为main.cpp。

ObjC block简析(一)

在main.cpp中可以发现上图中的代码。其中在main函数中的block和block(),被转化为

void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
复制代码

其中不乏强制转换类型的代码,将强制类型转换的代码去掉我们可以明确的看出:

void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
        block->FuncPtr(block);
复制代码

block存储了 __main_block_impl_0 函数的地址,而此函数中传递的第一个值是一个函数指针,函数的作用就是block中存储的代码的作用,即打印 hello world 。第二个参数是一个结构体指针,其中包含有对block的描述。

__main_block_impl_0 是一个结构体, __main_block_impl_0 函数是结构体 __main_block_impl_0 的构造函数,在此结构中就可以看到这个与结构体名字相同的没有返回值的函数。

该函数将类型地址赋值给isa,将存放block中代码的函数的指针赋值给funcptr,将block的描述赋值给desc。

__main_block_impl_0 结构体中第一个成员就是:

ObjC block简析(一)
我们可以看到这里并不是结构体指针,而是结构体本身,所以 __main_block_impl_0

的第一个成员其实是isa.

而我们的 block() 转换成了 block->FuncPtr(block); 就是通过block查找到FuncPtr然后调用,执行相应的代码。至于block明明是 __main_block_impl_0 类型的为什么却能直接查找到FuncPtr成员,上面已经说明这里存放的并不是结构体指针,也不是地址,而是结构体impl本身,所以block是可以经过类型强制转换转换成 __block_impl 类型进而通过FuncPtr指针调用相应的函数的。

我们都知道,ObjC中的对象都有一个isa指针,而block中同样存在isa指针。所以可以说block是一个ObjC指针。它封装了block函数的调用。

Block的变量捕获

像上述例子对我们来说通常没有什么实际作用,而我们通常要在block中处理一下外部变量的逻辑,那么block是怎么处理这些变量的呢?

auto局部变量的捕获

ObjC block简析(一)

出现上述情况的原因是什么呢?依然将main.m转成main.cpp。

ObjC block简析(一)

上面图片中我们可以发现block的结构体中出现了一个age变量,并且其构造函数表明将_age赋值给age成员。而在FuncPtr存储的函数 __main_block_func_0 的地址中,我们发现调用此函数是从block的结构体中取出age成员的值进行打印的。所以当定义block的时候age的值已经被block用一个同样名字的成员捕获了。而且捕获的仅仅是age的值。

static局部变量的捕获

ObjC block简析(一)

通过上图我们可以看出block中存储的是a的地址,所以当FuncPtr指向的函数调用的时候会通过取a地址中存储的值,所就出现下面这种情况了。

ObjC block简析(一)

全局变量的捕获

ObjC block简析(一)

我们可以看出全局变量并没有被block所捕获,因为全局变量存放在全局区,随时都可以访问,所以当FuncPtr指向的函数调用的时候就会直接取a和b的值用。而局部变量超过作用域就会自动回收所以block需要在自身存放一份,以保证其能准确访问。

总结

局部变量在block中使用的时候会被block捕获,auto变量是值捕获,而static变量是地址捕获。全部变量不会被捕获。

block类型

既然上面说block是一个oc对象,那么他也应该是有类型的。那么block的类型有什么有趣的事呢?

由于在ARC环境下编译器默默对block做了一些我们看不见的工作,所以我们将xcode的arc模式关掉,以便于窥探到本质。

ObjC block简析(一)
ObjC block简析(一)
ObjC block简析(一)

通过上述结果我们可以看出当block访问了auto变量的时候会变成 __NSStackBlock__ 类型。而其他情况下是 __NSGlobalBlock__ 类型。

__NSGlobalBlock__ 类型存在于数据区, __NSStackBlock__ 存在于栈区。

在ARC环境下打印的结果:

ObjC block简析(一)

而在ARC环境下原本 __NSGlobalBlock__ 的block依然是 __NSGlobalBlock__ 类型,而原本是 __NSStackBlock__ 却变成了 __NSMallocBlock__ 存放在堆区。这是因为当我们定义block的时候ARC默认为我们做了一次copy操作。

下面是block的类型以及在内存中存放的区域:

ObjC block简析(一)

我们尝试在MRC下对 __NSGlobalBlock____NSMallocBlock__ 类型的block进行copy操作并打印结果:

ObjC block简析(一)

block的类型(MRC环境)

访问了auto变量的block是 __NSStackBlock__ 类型,没有访问auto变量的block是 __NSGlobalBlock__ 类型。而对 __NSStackBlock__ 类型进行copy操作就会变为 __NSMallocBlock__ 类型。

对三种类型的block分别进行copy操作结果如下:

ObjC block简析(一)

[往深处看-Block(二)](


以上所述就是小编给大家介绍的《ObjC block简析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

零成本实现Web性能测试

零成本实现Web性能测试

温素剑 / 电子工业出版社 / 2012-2 / 59.00元

《零成本实现Web性能测试:基于Apache JMeter》是一本关于Web性能测试的实战书籍,读者朋友们在认真阅读完《零成本实现Web性能测试:基于Apache JMeter》后,相信能够将所学知识应用到生产实践中。《零成本实现Web性能测试:基于Apache JMeter》首先介绍基础的性能测试理论,接着详细介绍如何使用JMeter完成各种类型的性能测试。实战章节中作者以测试某大型保险公司电话......一起来看看 《零成本实现Web性能测试》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码