Block 探索之路

Block(初探1) 格式、类型

2019-12-16  本文已影响0人  三月木头
简括

Block的声明与赋值只是保存一段代码块,必须调用才能执行内部代码。
Block 底层就是一个struct结构体,所以它就是一个对象,作为结构体,里面会添加属性,导致block会有自动捕获变量的特性。

正是因为block有自动捕获变量特性,导致他的三种类型变化。我们正常声明一个block的时候,这个block其实是存在global区内存中,当这个对象进行自动捕获后,这个对象block会变成到stack区中,如果将这个block对象进行了赋值操作,也就是执行 赋值运算符=号操作(相当于copy一份进heap堆区),则会将这个带有捕获对象的stack区的block变为Heap区的block。下面我们会用代码 同打印 进行一一验证。

Block变量的赋值格式

Block变量的赋值格式是:myBlock变量 = ^返回值类型(参数列表){函数体};不过通常情况下都将返回值类型省略,因为编译器可以从存储代码块的变量中确定返回值的类型

int(^myBlock)(int) = ^(int num){
    return num * 7;
};
Block分为三类

按照内存分布,block分为三种。

Block类型 存在内存区域 内存指针开头
NSGlobalBlock 静态区 0x1
NSStackBlock stack区(栈区) 0x7
NSMallocBlock heap区 (堆区) 0x6
Block类型分别在什么情况下生成?
1. NSGlobalBlock生成
   void (^myBlcok)(void) = ^{
        NSLog(@"全局block");
    };
    NSLog(@"myBlock: %@", myBlcok);
    
    NSLog(@"只开辟生成的block: %@",^{
        NSLog(@"这个也是全局block");
    });

如上代码所示,我们自定义一个block,或者只是开辟一个block空间,没有对他进行运算符=号 的操作。我们尝试打印一下其内存地址。

2019-12-16 16:34:49.047725+0800 BlockTest[1713:160228] myBlock: <__NSGlobalBlock__: 0x103e22040>
2019-12-16 16:34:49.048129+0800 BlockTest[1713:160228] 只开辟生成的block: <__NSGlobalBlock__: 0x103e22060>

打印结果内存为0x1,类型NSGlobalBlock。

2. NSStackBlock 生成
    int a = 10;
    //block有捕获自动变量
    NSLog(@"只开辟生成的block: %@",^{
        NSLog(@"block捕获变量: %d",a);
    });

如上代码所示:我们开辟一个block,如果不捕获变量时候,这个blokc是存在静态区的NSGlobalBlock类型。那如果我们用此block自动捕获了对象,那么会变成什么样子呢?看下面打印。

2019-12-16 16:58:07.115068+0800 BlockTest[2062:239378] 只开辟生成的block: <__NSStackBlock__: 0x7ffee1df60e0>

通过打印,我们可以看出,具有捕获变量的NSGlobalBlock对象,变成了NSStackBlock类型对象。内存地址0x7

3. NSMallocBlock 生成
    int a = 10;
    //block有捕获自动变量
    void (^myBlcok)(void) = ^{
        NSLog(@"myblock: %d",a);
    };
    NSLog(@"myBlock: %@", myBlcok);

如上代码所示:我们开辟一个block,并给其命名myBlock,myBlock通过block特性自动捕获了变量a,那么现在的myBlock还同没有捕获变量a之前那样还是属于 NSGlobalBlock类型吗?我们看一下打印

2019-12-16 16:58:07.114845+0800 BlockTest[2062:239378] myBlock: <__NSMallocBlock__: 0x600000dcbd20>

通过打印可以看出,现在myBlock已经变成存在heap区的NSMallocBlock类型了。想想上一步,未初始的对象存在global区的NSGlobalBlock类型,经过自动捕获对象的存在stack区的NSStackBlock类型,如果我们在执行赋值运算符=号之后,现在存在的是heap区的NSMallocBlock类型了。
在这个过程中,发现了什么问题没有?
我们是在通过赋值运算符=号之后,发现stack区的block变成了heap区的block,这期间发生了什么呢?这样做的目的是什么呢?
初步思考,由于赋值后,我们这个会通过对象调用这块内存区域,如果我们这块内存还在栈中,万一我们在使用这个对象的途中,系统给我们释放了这块内存,那就会造成crash,这是万万不能允许的。所以从这方面考虑我们也应该需要把这个对象拷贝到heap区,让程序员来控制它何时释放。
下一片文章我们一起通过源码进行探索解析。

知识点补充:

没有用到外部变量肯定是_NSConcreteGlobalBlock,这点很好理解。不过只用到全局变量、静态变量的block也是_NSConcreteGlobalBlock。

上一篇下一篇

猜你喜欢

热点阅读