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简析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Build Your Own Web Site the Right Way Using HTML & CSS

Build Your Own Web Site the Right Way Using HTML & CSS

Ian Lloyd / SitePoint / 2006-05-02 / USD 29.95

Build Your Own Website The Right Way Using HTML & CSS teaches web development from scratch, without assuming any previous knowledge of HTML, CSS or web development techniques. This book introduces you......一起来看看 《Build Your Own Web Site the Right Way Using HTML & CSS》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试