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 栈上Block的结构体实例 __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。

当然还有

类 设置对象的存储域 _NSConcreteStackBlock 栈 _NSConcreteGlobalBlock 程序的数据区域(.data区) _NSConcreteMallocBlock 堆

什么情况下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

1 0
原创粉丝点击