block

本文深入探讨了Block在不同情况下的存储位置,包括访问全局变量、自动变量、对象时的差别,以及ARC和MRC环境下Block的行为变化。解析了Block如何截获变量值,何时需要使用__block修饰符,以及如何避免循环引用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 当block访问静态全局变量或者全局变量时候是ok的

    1. 因为:block的实质是函数,函数是可以直接访问全局变量或者静态全局变量的,这时候block存储在数据区。
  2. 当block什么都不访问时候,这时候该block也是存储在数据区。

  3. 当block访问自动变量时候

    1. block内部只是简单的使用了外部自动变量不涉及赋值。(这块其实oc内存管理这本书讲解的是有问题的,它没考虑到arc和mrc情况)
      0. 代码:

      //第一种情况
          int a = 3;
          void(^blk1)(void) = ^{
              int b = a+1;
          };
          
          //第二种情况
          NSLog(@"%@",^{int c = a + 5;});
      
      1. 结论:第一种情况下arc下该block存贮在堆区。mrc下该block存贮在栈区,只需考虑是否用copy将block从栈区复制到堆区。第二种情况下arc,mrc都是存贮在栈区。
      2. 原理: 这时候外部变量不用加__block,并且该自动变量是值传递,当block是存储在栈区时候,注意是否用copy该block。如果不copy出了作用域后该block将会释放。
      3. 详细:首先将block转化成为普通的函数。接下来调用结构体中的构造方法,将函数地址和自动变量的值传入。并用结构体中的impl结构体中的funcPtr来接收函数地址,并用外部结构体接收自动变量的值,并把impl结构体返回。当调用blk时候,blk找到funcPtr从而找到了函数的地址,并调用函数。函数内部调用外部结构体中的自动变量的值。
      4. 相关问题
        1. 问: 为什么block能够截获自动变量的值
        2. 答:因为该自动变量的本质是值传递。接下来答3.中详细内容。
    2. block内部涉及到对自动变量的赋值操作。

      1. 结论:将该自动变量用__block修饰。在第一种情况下arc下不用管因为存贮在堆区。mrc下是存在了栈区需要考虑该blcok是否调用copy方法。第二种情况下都是存贮在栈区,考虑是否调用copy方法。;
      2. 原理: 该自动变量要用__block修饰符来修饰(或者用static关键字修饰),该自动变量转换为结构体,并址传递。当block是存在栈区,考虑是否copy该结构体。(考虑该结构体释放问题)
      3. 详细:首先将__block变量转换为结构体,并初始化该结构体。并将block转化成为普通的函数。接下来调用结构体中的构造方法,将函数地址和该初始化后的结构体地址传入。外层结构体内部用impl结构体中的funcPtr接收该函数地址,外层结构体内部并用另外一个结构体接收该传入的结构体地址。
      4. 相关问题
        1. 问: 为什么该自动变量必须用__block修饰,不用不行吗。
        2. 答: 不用的话是值传递,值传递是值从一块内存中把值复制到另外一块内存,这时候不能从一块内存修改另一块内存的值。也就是值传递不能修改外部自动变量的值。加了__block后变成了址传递,这时候就可以修改外部变量的值了。
          并把该impl结构体返回。blk调用时候,blk找到funcPtr从而找到函数地址,并调用该函数。该函数内部访问外部结构中的存储初始化结构体中的结构体从而访问外部自动变量。
  4. 当block访问外部对象时候

    1. 当block内部只是简单的用外部对象
      1. 结论:第一种情况,arc下什么都不用管,因为存贮在堆区。mrc下是存贮在栈区,这时候该block应该调用copy方法。第二种情况下arc和mrc下都是存贮在栈区,应该调用copy方法(将改block从栈区复制到堆区,不然该外部对象将会释放)
      2. 原理: 该对象不用加__block,该对象是值传递。当block是存储在栈区,这时候该block应该用copy函数复制到堆区。不然该block可能释放,外部对象也可能释放。(因为栈区的block不能强引用对象)
      3. 详细: 其实和简单的截获自动变量一样。只不过现在用的是强引用来接收外部对象的值。
    2. 当block内部涉及到外部对象赋值操作
      1. 结论:该对象用__block修饰。第一种情况arc下什么都不用管,因为存贮在堆区。mrc下存在栈区应该调用copy。第二种情况下都是存贮在栈区应该调用copy方法。
      2. 原理: 该对象应该用__block修饰,是址传递,当block存贮在栈区的时候后,这时候该block应该用copy函数复制到堆区。不然该block可能释放,外部对象也可能释放。(因为栈区的block不能强引用对象)
      3. 详细:和简单截获外部对象一样,只不过现在 是址传递。
  5. 什么时候用copy什么时候不用

    1. 结论:
      1. 属性要用copy。
      2. 当第二种情况下访问自动变量了要考虑是否用copy当截获对象了一定要用copy。
      3. 当涉及到用了cocoaTouch框架的useingBlock时,或者block作为函数返回值时,或者用了gcd的api时候不用copy。
    2. 原理:
      1. 属性。属性如果不用copy,在mrc下当block访问了自动变量或者对象时候。这时候block是存在栈区。那么该block和外部对象都可能释放。所以要用copy属性。
      2. 当第二种情况下访问了自动变量时候,该block存贮在栈区,栈区当出了作用域后block就会释放,所以考虑是否用copy。当截获对象时候,该block也是存储在栈区,栈区无法强引用外部对象。所以应该用copy,复制到堆区来强应用外部对象,以防对象释放。
  6. __block对象

    1. __block类型的__weak其实和__weak没区别
    2. __block __autoreleasing编译会报错
    3. __block __strong 可以使用将对象转成结构体
  7. 循环引用

    1. 造成循环引用的原因
      1. 往往是引用环造成的循环引用
    2. 解决办法
      1. 如果这个block一定调用可以定义__block类型变量等于被引用的对象。在block里边用这个__block类型的变量,并且用完要记得赋值为nil。(如果这个block不调用会造成内存泄漏的!)
      2. 可以用__weak修饰符修饰。也可以用typeof定义
      3. ios4时候必须用__unsafe_unretained(ios4后不可以这么用,不然程序会崩溃的)
  8. block代码书写

    1. 简单的blk定义

      1. 代码
       void(^blk)(int,int) = ^(int a, int b){
              NSLog(@"%d",a+b);
          };
      
      1. 解释
      返回值类型(^名字)(入参类型)= ^(入参列表,当没有参数可以省略小阔号){
          里边书写blk代码,如果有返回值可以用return 返回
      };
      
    2. 用typedef定义block

      1. 代码
      typedef void(^Blk)(int a, NSString *b);
      
      1. 解释
      typedef 返回值类型(^名字)(形参列表);
      
    3. 属性中用block

      1. 代码
      @property (copy, nonatomic) Blk blk1;
      
      1. 往往是一个类的.h文件中定义typedef类型的blk,之后再定义属性blk。最后往往是当这个类被事例成一个对象时候往往会给这个属性赋值一个blk,之后再该.m文件中调用外界传来的属性blk.
    4. block作为方法的参数返回

      1. 代码
      - (void)getImg:(void(^)(NSError *error,UIImage *img))finishBlk{
          //这里会进行网路请求,请求图片。请求结束后会调用finishBlk把请求结果返回给调用者
      }
      
      1. 解释
      实质上是外部调用该方法时候传入了一个block。这个block在方法内部执行了。但是我们也可以理解为该方法的作用是把网络请求的结果利用blk返回给调用者。
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值