block笔记

436 阅读2分钟

block 三种类型

全局堆区栈区
没有访问auto变量栈区经过copy访问了auto变量
  • 如果block访问外界变量,并进行block相应拷贝,即copy
    • 如果此时的block是强引用,则block存储在堆区,即堆区bloc
    • 如果此时的block通过__weak变成了弱引用,则block存储在栈区,即栈区block
  • MRC环境下 访问了auto变量,并没有copy,即还是栈区block
 int g = 666;
 void (^ __weak block) (void) = ^ {
     NSLog(@"%d",a);
 }

block变量捕获底层

  • 全局变量
    • 直接访问,在全局区域。不需要__block修饰,即可改变值
  • 局部变量
    • auto变量,捕获到block内部,值捕获 (需要__block修饰才能改变外部变量的值)
    • static,捕获到block内部,指针捕获。不需要__block修饰,即可改变值

关于__block底层实现

  • __block用于解决block内部无法修改auto变量值的问题
  • __block不能修改全局变量、静态变量
  • __block原理
    • 编译器会将外界变量会生成__Block_byref_a_0结构体(对象)
    • 结构体用来保存原始变量的指针和值
    • 将变量生成的结构体对象的指针地址 传递给block,然后在block内部就可以对外界变量进行操作了
    __block int a = 666;
    void(^block)(void) = ^{
        a--;
        printf("%d", a);
    };
    
     block();
     
     //底层编译
     struct __Block_byref_a_0 {//__block修饰的外界变量的结构体
       void *__isa;
       __Block_byref_a_0 *__forwarding;//指向自身的指针 age->__forwarding->ag
      int __flags;
      int __size;
      int a;
     };
     
     //Block
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_age_0 *age; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
      impl.isa = &_NSConcreteStackBlock;
      impl.Flags = flags;
      impl.FuncPtr = fp;
      Desc = desc;
  }
};

block循环引用

  • self持有block,block内部捕获了self,block持有self,互相引用,不能释放
  • 解决循环引用
    • self为block参数
    • 使用NSProxy(参考YYWeakProxy)
    • __weak
    __weak typeof(self) weakSelf = self;
    
    • __block修饰对象(需要注意的是在block内部需要置空对象,而且block必须调用)
    __block ViewController *vc = self;
    self.block = ^(void){
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          vc = nil;//手动释放
      });
    };
    self.block();
    

block本质

  • 是一个oc对象,内部也有isa指针
  • 封装了函数调用以及函数调用环境的OC对象