Block存储域探析
来源:互联网 发布:第二梦软件 编辑:程序博客网 时间:2024/06/05 12:48
接《Block截获自动变量实现与__block修饰符内部实现》我们继续探讨Block
留下的问题
- 1,
__Block_byref_i_0 *__forwarding
;这个指向自身的指针是什么鬼,有什么作用,什么时候用? - 2,Desc_0结构体中多出来的
void (*copy) void (*dispose)
这两个方法有什么作用,什么时候用?
Block 和 __block变量的实质
Block类型
struct __ViewController__viewDidLoad_block_impl_0 { struct __block_impl impl; struct __ViewController__viewDidLoad_block_desc_0* Desc; __Block_byref_i_0 *i; // by ref __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};
我们都知道isa指针是指向其class的指针,所以, impl.isa = &_NSConcreteStackBlock;
也就表示这个Block为_NSConcreteStackBlock
类型。即栈上的Block。
当然还有
什么情况下Block是 _NSConcreteGlobalBlock 类型的
根据名称,global(全局)我们创建一个全局的Block,看一下他的编译结果
#import "ViewController.h"//全局的Blockvoid (^blk)(void) = ^{NSLog(@"123");};typedef void(^WxsBlock) ();@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; blk();}@end
编译后
struct __blk_block_impl_0 { struct __block_impl impl; struct __blk_block_desc_0* Desc; __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteGlobalBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __blk_block_func_0(struct __blk_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_fbc035_mi_0);}static struct __blk_block_desc_0 { size_t reserved; size_t Block_size;} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);void (*blk)(void) = ((void (*)())&__global_blk_block_impl_0)
我们发现impl.isa = &_NSConcreteGlobalBlock;
我们再从block作为普通局部变量、函数返回值、函数参数时来看它的类型
有自动变量的截取
@property(nonatomic, assign) blk_t blockAssgin;@property(nonatomic, strong) blk_t blockStrong;@property(nonatomic, copy) blk_t blockCopy;blk_t getBlk_t(int i) { blk_t block4 = ^(int count){return count*i;}; NSLog(@"作为函数返回值,有自动变量截取---%@",block4); return block4;}{ //作为普通变量, 有自动变量截取 blk_t block1 = ^(int count) { return count * value; }; NSLog(@"作为普通变量, 有自动变量截取---%@",block1); //作为assign类型成员变量 ,有自动变量截取 self.blockAssgin = ^(int count) { return count * value; }; NSLog(@"作为assign类型成员变量 ,有自动变量截取---%@",self.blockAssgin); //作为strong类型成员变量 ,有自动变量截取 self.blockStrong = ^(int count) { return count * value; }; NSLog(@"作为strong类型成员变量 ,有自动变量截取---%@",self.blockStrong); //作为copy类型成员变量 , 有自动变量截取 self.blockCopy = ^(int count) { return count * value; }; NSLog(@"作为copy类型成员变量 , 有自动变量截取---%@",self.blockCopy); //作为函数返回值,有自动变量截取 getBlk_t(value); //作为函数参数,有自动变量截取 [self actionBlock:^int(int i) { return i*value; }];}- (void)actionBlock:(blk_t)block { //作为函数参数,无自动变量截取 NSLog(@"作为函数参数,有自动变量截取---%@",block); block(1);}NSLog 日志:Block[47786:5956467] 作为普通变量,有自动变量截取---<__NSMallocBlock__: 0x60000024c9c0>Block[47786:5956467] 作为assign类型成员变量,有自动变量截取---<__NSStackBlock__: 0x7fff5fbc39e0>Block[47786:5956467] 作为strong类型成员变量,有自动变量截取---<__NSMallocBlock__: 0x608000056110>Block[47786:5956467] 作为copy类型成员变量,有自动变量截取---<__NSMallocBlock__: 0x608000056620>Block[47786:5956467] 作为函数返回值,有自动变量截取---<__NSMallocBlock__: 0x6080000565c0>Block[47786:5956467] 作为函数参数,有自动变量截取---<__NSStackBlock__: 0x7fff5fbc3968>
但是通过clang编译出来的结果,他们全部同样全部都是impl.isa = &_NSConcreteStackBlock;
惊不惊喜?意不意外?
无自动变量的截取
{ //作为返回值,无自动变量截取 blk_t blockt = getBlk_t(); NSLog(@"作为返回值,无自动变量截取blockt---%@",blockt); //作为普通变量,无自动变量截取,此处尝试了@property(strong,assgin,copy)这三种情况,是相同的结果 self.blocktest = ^(int count){return count;}; NSLog(@"作为普通变量,无自动变量截取----%@",self.blocktest); //作为函数参数,无自动变量截取 [self actionBlock:^int(int i) { NSLog(@"%d",i); return i; }];}- (void)actionBlock:(blk_t)block { //作为函数参数,无自动变量截取 NSLog(@"作为函数参数,无自动变量截取---%@",block); block(1);}打印结果Block[47535:5919554] 作为返回值,无自动变量截取blockt---<__NSGlobalBlock__: 0x1039fd0f0>Block[47535:5919554] 作为普通变量,无自动变量截取----<__NSGlobalBlock__: 0x1039fd150>Block[47535:5919554] 作为函数参数,无自动变量截取---<__NSGlobalBlock__: 0x1039fd190>全部是GlobalBlock
但是通过clang编译出来的结果,他们全部同样全部都是impl.isa = &_NSConcreteStackBlock;
惊不惊喜?意不意外?
通过clang的编译在ARC下只能看到静态情况下(静态代码声明)的Block类型,感觉只能作为参考,毕竟还有运行时。
所以我们可以根据NSLog的打印的结果简单总结一下:
Block类型 有无自动变量截取 使用情况 _NSConcreteGlobalBlock 有 当作全局变量声明并初始化 无 全部情况 NSStackBlock 有 assgin类型的成员变量 作为函数参数 无 无 _NSConcreteMallocBlock 有 作为普通的局部变量 作为copy类型的成员变量 作为strong类型的成员变量 作为函数返回值 无 无
为什么_NSConcreteGlobalBlock类型的Block要设置在数据区域?
因为在使用全局变量的地方不会截获自动变量。因为它在一开始就已经定义好了。不存在”变量“,所以它的执行在任何时候都是固定的,不会依赖运行时的环境,这种类型的操作全局只有一个就可以,既节省空间又好管理。所以放在全局静态区的数据区域最好不过了。
通过clang编译和NSLog的对比的总结
通过clang编译的结果可知,除了在全局变量声明并创建的时候block被编译成_NSConcreteGlobalBlock
其他时候全部都是_NSConcreteMallocBlock
类型,我们不难看出,Block的初始状态只有这两种,所以Malloc状态的Block是从Stack状态转变过去的,这个猜想在看《Objective-C高级编程》的时候也和作者不谋而合。
那么我们就来探讨一下它是如何从栈被搞到堆中去的。
根据《Objective-C高级编程》的描述:
它是通过objc_retainBlock()
方法来把Block从栈搞到堆中的,
通过objc4运行库的runtime/objc-arr.mm 可知: objc_retainBlock()
= _Block_copy
当一个Block被作为函数返回值时为什么会被搞到堆中,它被做了什么?
blk_t func(int rate) { /* 通过Block语法生成Block,配置在栈上的结构体实例, tmp被赋值 */ blk_t tmp = &__func_block_impl_0(...); /* 通过_Block_copy将其复制到堆上 复制后将堆上的地址作为指针复制给变量tmp */ tmp = _Block_copy(tmp); /* 将堆上的Block作为Objective—C对象 注册到autoreleasepool中,返回该对象 */ return objc_autoreleaseReturnValue(tmp);}
在ARC中Block被复制的操作是编译器自己搞定的,但是编译器也不是万能的
当作为返回值的时候,编译器显然是可以搞定的
但是再作为函数参数的时候就不太好使了,这个时候需要我们手动的去copy Block,这一点通过“作为函数参数,有自动变量截取”验证实例得到了验证,它都是stack类型的
这里肯定会被问,为什么非得copy到堆上??在栈上就不行?
俩字:安全!
复制到堆上的__block变量和Block变量在变量作用域结束时不受影响
什么时候需要手动Copy
向方法或者函数的参数重传递Block时
但是如果在方法或者函数中适当的复制了传递来的参数,那么就不必在调用该方法或者函数前手动复制了,如一下方法:
1,Cocoa框架的方法且方法名字中有usingBlock等时
2,GCD的API
- Block存储域探析
- Block存储域
- Java中的double类型数据存储探析
- 存储系统的实现-探析存储的机制和原理
- block 存储循环
- block存储位置问题
- SIP 探析
- SIP 探析
- CALayer 探析
- SIP 探析
- ScheduledExecutorService 探析
- SIP 探析
- SIP 探析
- 广播探析
- Block
- block
- Block
- block
- mxnet系列 全连接层代码阅读
- 从远程git服务器上导入项目到本地
- java第三方,邮件,短信功能
- 深入理解java虚拟机--OutOfMemoryError
- java 参数类型Object...
- Block存储域探析
- HTTP协议详解
- 用opencsv和用Bufferedreader/writer进行csv文件的读写及简单处理
- Tensorflow中reshape()函数的使用
- 生成式对抗网络---机器学习中的热门话题
- 参数传值,传引用,传值,传指针
- 【Linux】VMware下安装CentOS
- Java 自定义方法
- CSS关于选择器 :nth-child(n)