oc-block相关

oc-block相关

IOS小彩虹2021-08-21 10:45:45160A+A-

一、三种block

1、全局block

- (void)viewDidLoad
{
  [super viewDidLoad];
  
  void (^block)(void) = ^{
    NSLog(@"hehe");
  };
  NSLog(@"%@",block); // 打印block对象
}

--------------------
打印:
  <__NSGlobalBlock__:0x105f14088> //说明这是一个全局block

2、堆block

- (void)viewDidLoad
{
  [super viewDidLoad];
  
  int a = 10;
  void (^block)(void) = ^{
    NSLog(@"hehe - %d",a);
  };
  NSLog(@"%@",block); // 打印block对象
}

--------------------
打印:
  <__NSMallocBlock__:0x600002cceeb0> //说明这是一个堆block

3、栈block

- (void)viewDidLoad
{
  [super viewDidLoad];
  
  int a = 10;
  
  NSLog(@"%@",^{
    NSLog(@"hehe - %d",a);
  }); // 打印block对象
}

--------------------
打印:
  <__NSStackBlock__:0x7ffee09cf800> //说明这是一个堆block
    
原因:代码块在做=的赋值操作的时候隐藏了一个copy,从栈区拷贝到的堆区

栈:0x7

堆:0x6

静态变量:0x1

二、block的循环引用

1、引用计数与对象的释放

  1. 当对象A引用对象B时,B的rentainCount +1
  2. 当A进行dealloc时,向B发送release信号,B的retainCount -1
  3. 当B的retainCount == 0时,B就会调用dealloc

2、循环引用

A引用了B,此时B的rentainCount==1,然后B引用了A,此时A的rentainCount==(n+1),所以A和B都无法调用dealloc

typedef void(^HHBlock)(void);
@interface ViewController ()
@property (nonatomic , copy) HHBlock block;
@property (nonatomic , copy) NSString *name;
@end
 
@implementation ViewController
 
// 循环引用版
- (void)viewDidLoad
{
  [super viewDidLoad];
  
  self.name = @"hehe";
  self.block = ^{
    self.name = @"haha";
  };
  
  /*
  此时,self 持有block,同时,block持有self
  */
  self.block;
}

// 升级版
- (void)viewDidLoad
{
  [super viewDidLoad];
  
  self.name = @"hehe";
  
  __weak typeof(self) weakSelf = self;
  // 此处开始self引用计数不会因为block持有而增加
  self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;
    // 此处临时变量会在autoreleasepool中自动释放
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
      strongSelf.name = @"haha";
    });
  };
  self.block;;
}

// 升级版 2
- (void)viewDidLoad
{
  [super viewDidLoad];
  
  self.name = @"hehe";
  
  __block ViewController *vc = self;
  // __block可以copy一份当前的对象到新建的struct里面
  // 目前self持有block持有vc持有self
  self.block = ^{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
      vc.name = @"haha";
      vc = nil; //直接给变量释放,不置为nil还是循环引用不能自动释放
      //目前self持有block持有nil不再持有self
    });
  };
  self.block;;
}

- (void)dealloc
{
  NSLog(@"调用了dealloc");
}
// 骚骚版 3
typedef void(^HHBlock)(ViewController *);
@interface ViewController ()
@property (nonatomic , copy) HHBlock block;
@property (nonatomic , copy) NSString *name;
@end
 
@implementation ViewController
 
- (void)viewDidLoad
{
  [super viewDidLoad];
  
  self.name = @"hehe";
  self.block = ^(ViewController *vc){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
      vc.name = @"haha";
    });
  };
  
  self.block(self);
}

- (void)dealloc
{
  NSLog(@"调用了dealloc");
}

三、bolck自动捕获变量的特性

1、引出问题

  • 对象的底层:struct
  • block是个对象
  • block捕获变量 -->即--> struct内部增加属性

NSLog的本质是什么?

答:是一套封装了print的C语言函数,比较耗时

- (void)viewDidLoad
{
  __block int a = 10;
  NSLog(@"进去之前:%p",&a); // 0x7ffee3deb988 - 栈
  viod(^block)(void) =^{
    // 捕获
    a++;
    NSLog(@"在里面:%p",&a); // 0x600002647c78 - 捕获后 到 堆区
  };
  NSLog(@"写完block:%p",&a); // 0x600002647c78 - 看写代码的顺序,不要看调用顺序
  block();
}

2、源码初探

// C语言
#include "stdio.h"

int main() {
  void(^block)(void) = ^{
    printf("hehe");
  };
  block;
  
  return 0;
}

---------------------- 执行clang看其内部实现
clang -rewrite-objc testBlcok.c -o blockCPP.cpp
---------------------- 结果很乱但是不用看,看下面简化版的就行
int main() {
  void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
  ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

  return 0;
}
---------------------- 简化版
int main() {
  void(*block)(void) = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA); // f(a,b)
  block->FuncPtr(block); // 其实就是 block();

  return 0;
}
// __main_block_impl_0
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
  {
    // 初始化
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    // 保存代码块的能力
    impl.FuncPtr = fp; // 属性函数 - 保存函数 - __main_block_func_0 其实就是^{ ··· }代码块
    Desc = desc;
  }
};

3、捕获变量的代码

  1. 不加__block的int

    #include "stdio.h"
    
    int main() {
      int a = 10;
      void(^blockNN)(void) = ^{
        printf("--- %d",a);
      };
      blockNN();
      
      return 0;
    }
    ------------
    int main() {
      int a = 10;
      void(*blockNN)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
      ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN);
    
      return 0;
    }
    ------------
    int main() {
      int a = 10;
      void(*blockNN)(void) = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA, a)); // f(a,b,c)
      
      (blockNN->FuncPtr)(blockNN);
    
      return 0;
    }
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      
      
      
      int a; // 自动捕获到了变量
      
      
      
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) 
    {
      // 创建新的变量
      // __cself就是(blockNN->FuncPtr)(blockNN)中的blockNN
      int a = __cself->a; // bound by copy 
    
        printf("--- %d",a);
      }
    
  2. 加__block的int

    #include "stdio.h"
    
    int main() {
      __block int a = 10;
      void(^blockNN)(void) = ^{
        printf("--- %d",a++);
      };
      blockNN();
      
      return 0;
    }
    ----------------
    int main() {
      __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
      void(*blockNN)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
      ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN);
    
      return 0;
    }
    -----------------
    int main() {
      // 生成了一个含有五个属性的结构体
      __Block_byref_a_0 a = {
        0,
        &a, // 引用原来的a的地址
        0,
        sizeof(__Block_byref_a_0),
        10  // 原来的a的值
      };
      
      
      void(*blockNN)(void) = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA, &a, 570425344));
      // 其中的&a就是上面结构体的指针地址
      
      
      ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN);
    
      return 0;
    }
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      
      
      __Block_byref_a_0 *a; // by ref
      // 自动捕获__Block_byref_a_0类型的*a的指针
      
      
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) 
    {
      // 指针传递
      __Block_byref_a_0 *a = __cself->a; // bound by ref
      // 此*a就是原来的结构体__Block_byref_a_0 a,包含了原来a的地址和值
    
        printf("--- %d",(a->__forwarding->a)++);
      }
    
  3. 总结

    没有__block修饰的int a只是传进来一个值,没有原来的a的地址,不能修改,是只读状态

    __block修饰的int a就是指针传递,可读可写

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们