zoukankan      html  css  js  c++  java
  • iOS-block三种类型详解

    Block有三种类型:__NSGlobalBlock,__NSStackBlock,__NSMallocBlock

    问题:
    Block有几种类型呢?这几种类型分别在什么情况下出现?
    我们思考一下,__NSStackBlock在访问外部变量时,会有什么问题? 

       我们在讲block的本质的时候已经知道了,block的本质就是一个 OC 对象,那么既然它是一个 OC 对象,它就会有类型,本文就将讲解block的三种类型.并都继承于NSBlock
    我们在讲block的三种类型之前,先了解一下程序的内存分配情况,因为不同类型的block分配的内存也不同.

    • .text段 : 也称代码段,我们写的代码都存放在这里
    • .data区 : 也称数据区,一般存放全局变量, __NSGlobalBlock存放在这里
    • 堆区 : 存放我们自己alloc出来的对象,动态分配内存,需要程序员自己申请内存,自己管理. __NSMallocBlock存放在堆区
    • 栈区 : 一般存放局部变量,不需要程序员管理,系统自动分配,自动销毁,__NSStackBlock存放在栈区
       
      不同block类型的内存分配

    一: __NSGlobalBlock
     结论: 没有访问 auto变量 的block 就是 __NSGlobalBlock

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            static int age = 10;
            void(^block)(void) = ^{
                NSLog(@"Hello, World! %d",age);
            };
            NSLog(@"%@",[block class]);
        }
        return 0;
    }
    控制台输出:__NSGlobalBlock__

    这个很好理解,不过多解释

    二: __NSStackBlock
    结论:访问了auto变量 的block 就是 __NSStackBlock

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            int age = 10;
            void(^block)(void) = ^{
                NSLog(@"Hello, World! %d",age);
            };
            NSLog(@"%@",[block class]);
        }
        return 0;
    }
    控制台输出:__NSMallocBlock__

    怎么打印的是__NSMallocBlock__,刚才不是说访问了auto变量就是__NSStackBlock吗?
    因为这里我们使用的是ARC,在ARC环境下,Xcode编译器再某些情况会默认帮我们做调用copy 变成堆block ,我们在Build Settings中把ARC设置成MRC,再来打印一下:

    2018-08-30 17:37:09.846365+0800 block的类型[4318:3463149] __NSStackBlock__
    

    这次打印的就是__NSStackBlock__

    我们思考一下,__NSStackBlock在访问外部变量时,会有什么问题? 

    会出现野指针crash 所以在ARC坏境Xcode帮我们处理成了堆block(__NSMallocBlock__)防止出现释放了还去访问导致野指针crash

     

    所以,为了避免出现这种情况,我们需要把block存储在堆上,__NSMallocBlock就闪亮登场了.

    三: __NSMallocBlock
    结论: 当一个__NSStackBlock调用了copy操作,返回的就是一个__NSMallocBlock

    思考:如果我们对__NSStackBlock 进行一次 copy操作,会发生什么变化呢?

     __NSStackBlock copy后

      

    ‼️注意

    以上都是在MRC环境下
    如果是在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上, 比如以下几种情况:

    • 1. block作为函数返回值时

      block作为返回值编译器会自动copy
    • 2.将block赋值给__strong指针时

      被强指针引用的block会自动copy
    • 3.block作为Cocoa API方法名含有UsingBlock的方法参数时

      UsingBlock
    • 4.block作为GCD API的方法参数时
            GCD API的方法参数
     
    •  5.block调用copy方法
     
    三种类型block的内存存储以及每一种类型的block调用copy后的结果如下所示:

    总结:

    • 1:一共有三种类型的Block.分为__NSGlobalBlock,__NSStackBlock,__NSMallocBlock.
      1. 没有访问 auto变量 的block 就是 __NSGlobalBlock
      2. 访问了auto变量 的block 就是 __NSStackBlock
      3. 当一个__NSStackBlock调用了copy操作,返回的就是一个__NSMallocBlocksing
    • 2:在ARC环境下,编译器会自动把栈上的block copy到堆上


  • 相关阅读:
    RocketMQ学习笔记(14)----RocketMQ的去重策略
    RocketMQ学习笔记(13)----RocketMQ的Consumer消息重试
    RocketMQ学习笔记(12)----RocketMQ的Consumer API简介
    RocketMQ学习笔记(11)----RocketMQ的PushConsumer和PullConsumer
    RocketMQ学习笔记(10)----RocketMQ的Producer 事务消息使用
    RocketMQ学习笔记(9)----RocketMQ的Producer 顺序消息
    RocketMQ学习笔记(8)----RocketMQ的Producer API简介
    RocketMQ学习笔记(7)----RocketMQ的整体架构
    RocketMQ学习笔记(6)----RocketMQ的Client的使用 Producer/Consumer
    RocketMQ学习笔记(5)----RocketMQ监控平台rocketmq-console-ng的搭建
  • 原文地址:https://www.cnblogs.com/junhuawang/p/14951598.html
Copyright © 2011-2022 走看看