美文网首页Block 探索之路
Block(初探1) 格式、类型

Block(初探1) 格式、类型

作者: 三月木头 | 来源:发表于2019-12-16 17:47 被阅读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区,让程序员来控制它何时释放。
下一片文章我们一起通过源码进行探索解析。

知识点补充:

  • _NSConcreteStackBlock:
    只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
    StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。

  • _NSConcreteMallocBlock:
    有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制

  • _NSConcreteGlobalBlock:
    没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。

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

相关文章

  • Block(初探1) 格式、类型

    简括 Block的声明与赋值只是保存一段代码块,必须调用才能执行内部代码。Block 底层就是一个struct结构...

  • 10.26闭包

    importFoundation /* c语言block的格式: 返回值类型(^block变量名)(参数类型1:参...

  • block初窥

    一.block的简单使用 1.block声明 block变量的声明格式为: 返回值类型 (^block名字)(参数...

  • 每日一问01——block

    小白篇——基本用法 block声明——格式 返回类型(^名字)(参数列表) block表达式——格式 ^返回类型(...

  • 面试积累

    主要的几个大模块 1、block 一、 Block变量的声明格式为: 返回值类型(^Block名字)(参数列表);...

  • Block 用法小结

    1.Block的定义格式 返回值类型(^block变量名)(形参列表) = ^(形参列表) {};调用Block保...

  • (IOS)Block

    1、Block的声明 格式: 返回值 (^Block名称) (参数类型); 举例: //无参无返回值的Bloc...

  • Block (存放代码的块)

    block 数据类型 定义格式: 返回值类型 ( ^block 变量名 ) ( 形参列表 ) = ^( 形参列表 ...

  • Block 使用

    Block的定义: Block的格式: ^ + 返回值类型(可以省略) + 参数(如果没有参数,可以省略) + {...

  • 9 Block详解

    1.明白如何定义block类型 定义Block类型: typedef 返回值类型 Block名字 参数 block...

网友评论

    本文标题:Block(初探1) 格式、类型

    本文链接:https://www.haomeiwen.com/subject/cocrnctx.html