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简析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Traction: A Startup Guide to Getting Customers
Gabriel Weinberg、Justin Mares / S-curves Publishing / 2014-8-25 / USD 14.99
Most startups end in failure. Almost every failed startup has a product. What failed startups don't have is traction -- real customer growth. This book introduces startup founders and employees to......一起来看看 《Traction: A Startup Guide to Getting Customers》 这本书的介绍吧!