ObjC block简析(一)
栏目: Objective-C · 发布时间: 5年前
内容简介:在main.m的main函数中声明一个block并执行在main.cpp中可以发现上图中的代码。其中在main函数中的block和block(),被转化为其中不乏强制转换类型的代码,将强制类型转换的代码去掉我们可以明确的看出:
在main.m的main函数中声明一个block并执行 block()
通过 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
命令将main.m转为main.cpp。
在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
结构体中第一个成员就是:
__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局部变量的捕获
出现上述情况的原因是什么呢?依然将main.m转成main.cpp。
上面图片中我们可以发现block的结构体中出现了一个age变量,并且其构造函数表明将_age赋值给age成员。而在FuncPtr存储的函数 __main_block_func_0
的地址中,我们发现调用此函数是从block的结构体中取出age成员的值进行打印的。所以当定义block的时候age的值已经被block用一个同样名字的成员捕获了。而且捕获的仅仅是age的值。
static局部变量的捕获
通过上图我们可以看出block中存储的是a的地址,所以当FuncPtr指向的函数调用的时候会通过取a地址中存储的值,所就出现下面这种情况了。
全局变量的捕获
我们可以看出全局变量并没有被block所捕获,因为全局变量存放在全局区,随时都可以访问,所以当FuncPtr指向的函数调用的时候就会直接取a和b的值用。而局部变量超过作用域就会自动回收所以block需要在自身存放一份,以保证其能准确访问。
总结
局部变量在block中使用的时候会被block捕获,auto变量是值捕获,而static变量是地址捕获。全部变量不会被捕获。
block类型
既然上面说block是一个oc对象,那么他也应该是有类型的。那么block的类型有什么有趣的事呢?
由于在ARC环境下编译器默默对block做了一些我们看不见的工作,所以我们将xcode的arc模式关掉,以便于窥探到本质。
通过上述结果我们可以看出当block访问了auto变量的时候会变成 __NSStackBlock__
类型。而其他情况下是 __NSGlobalBlock__
类型。
__NSGlobalBlock__
类型存在于数据区, __NSStackBlock__
存在于栈区。
在ARC环境下打印的结果:
而在ARC环境下原本 __NSGlobalBlock__
的block依然是 __NSGlobalBlock__
类型,而原本是 __NSStackBlock__
却变成了 __NSMallocBlock__
存放在堆区。这是因为当我们定义block的时候ARC默认为我们做了一次copy操作。
下面是block的类型以及在内存中存放的区域:
我们尝试在MRC下对 __NSGlobalBlock__
和 __NSMallocBlock__
类型的block进行copy操作并打印结果:
block的类型(MRC环境)
访问了auto变量的block是 __NSStackBlock__
类型,没有访问auto变量的block是 __NSGlobalBlock__
类型。而对 __NSStackBlock__
类型进行copy操作就会变为 __NSMallocBlock__
类型。
对三种类型的block分别进行copy操作结果如下:
[往深处看-Block(二)](
以上所述就是小编给大家介绍的《ObjC block简析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!