美文网首页
Block 有三种类型

Block 有三种类型

作者: 昵称nickName | 来源:发表于2018-08-09 10:52 被阅读0次

    内存五大内存分区-----堆、栈、自由存储区、全局/静态存储区和常量存储区.
    1. 堆,就是那些由new分配的内存块,它们的释放编译器不管,由我们的应用程序控制
    2. 栈,就是那些由编译器在需要的时候分配,不需要的时候自动清除的变量的存储区。里面的变量通常是局部变
    量、函数参数等。
    3. 全局区(静态区) (static) 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区
    域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后有系统释放。
    4. 文字常量区 存放常量字符串,程序结束后由系统释放
    5. 程序代码区 存放函数的二进制代码

    Block 有三种类型,分别是:

    __NSConcreteStackBlock ————————栈中
    __NSConcreteGlobalBlock ————————数据区域中
    __NSConcreteMallocBlock ————————堆中

    ARC下NSStackBlock已经基本不会出现的了, 除非可以强行让它出现,但实际开发中应该不会做这么无聊的事。

    先看看一个DEMO。

    - (void)viewDidLoad {
    [superviewDidLoad];
    
    void(^blockA)() = ^{
    NSLog(@"just a block");
        };
    NSLog(@"%@", blockA);
    
    int value = 10;
        void(^blockB)() = ^{
    
    NSLog(@"just a block === %d", value);
        };
    NSLog(@"%@", blockB);
    
    void(^ __weak blockC)() = ^{
    NSLog(@"just a block === %d", value);
        };
    
    NSLog(@"%@", blockC);
    }
    

    三个NSLog打印的内容为:

    <__NSGlobalBlock__: 0x105a730d0>
    <__NSMallocBlock__: 0x600000052c00>
    <__NSStackBlock__: 0x7fff5a18b7e0>
    

    注意看它们的地址,NSGlobalBlock的地址明显要短,因为它是在进程数据段上的。
    blockC则是强行用__weak声明让其分配在栈上,这里会看到一个黄色的警告,大意就是指分配后就会被释放。就是说viewDidLoad这个方法return后这个block就会被释放。

    动态分配和静态分配的区分是在哪里?观察一下就发现NSGlobalBlock类型是没有捕获局部变量的,它只是打印一一个字符串。通过NSString literal创建的字符串是放在常量区的,也就是数据段上。全局的block里没有引用任何堆或栈上的数据。另外如果将上面的例子中的int value = 10;改为const int value = 10;那么blockB将变成NSGlobalBlock,这是因为const修饰下value里的值会存储在常量区即数据段上,也就是不违反原则,只要block literal里没有引用栈或堆上的数据,那么这个block会自动变为NSGlobalBlock类型,这是编译器的优化。

    在属性声明上,我们一般会用copy修饰一个Block属性。原因是什么?
    在MRR或MRC(两个词都是指同一个玩意)中,block默认是在栈上创建的。如果我们将它赋值给一个成员变量,如果成员变量没有被copy修饰或在赋值的时候没有进行copy,那么在使用这个block成员变量的时候就会崩溃。

    思考一下下面的代码。

    @property(nonatomic, weak) void(^block)();
    
    - (void)viewDidLoad {
    [superviewDidLoad];
    
    int value = 10;
    void(^blockC)() = ^{
    NSLog(@"just a block === %d", value);
         };
    
    NSLog(@"%@", blockC);
         _block = blockC;
    
    }
    
    - (IBAction)action:(id)sender {
    NSLog(@"%@", _block);
    }
    

    这代码在MRC下是会崩溃的。但ARC下就不会了,因为block默认就创建在堆上了。但是不是意味着ARC不用写copy来修饰block属性呢?当然不是了,上面已经说了,我们是可以强行在ARC上将一个block创建在栈上的。

    相关文章

      网友评论

          本文标题:Block 有三种类型

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