Block

68 阅读2分钟
  1. Block介绍
  2. 截获变量
  3. __block修饰符
  4. Block的内存管理
  5. Block的循环引用

什么是Block?

  • Block是将函数及其执行上下文封装起来的对象。 image.png

源码解析

  • 使用 [clang -rewrite-objc file.m]查看编译之后的内容
什么是Block的调用?
  • (即函数调用)
{
  int multiplier = 6;
  int(^Block)(int) = ^int(int num){
  return num*multiplier;
  };
  Block(2);
}

截获变量

{
  int multiplier = 6;
  int(^Block)(int) = ^int(int num){
  return num*multiplier;
  };
  multiplier = 4;
  NSLog(@"result is %d",Block(2));
}
// =>result是12.
  • 局部变量(基本数据类型,对象类型)
  • 静态局部变量
  • 全局变量
  • 静态全局变量
  • 对于基本数据类型的局部变量截获其值
  • 对于对象类型的局部变量连同所有权修饰符一起截获
  • 以指针形式截获局部静态变量
  • 不截获全部变量、静态全局变量

源码解析

  • 使用 [clang -rewrite-objc -fobjc-arc file.m]命令查看

__block修饰符

一般情况下,对被截获变量进行赋值操作需添加__block修饰符

image.png

笔试题的坑

image.png

  • 上图无需添加__block image.png
  • 上图需添加__block

对变量进行赋值时

image.png

image.png

__block相关的笔试题

image.png

__block修饰的变量变成了对象

image.png

image.png

  • 栈上的_forwarding指针指向自己。
__forwarding指针是用来干什么的?

Block的内存管理

  • impl.isa = &_NSConcreteStackBlock;

  • _NSConcreteGlobalBlock

  • _NSConcreteStackBlock

  • _NSConcreteMallocBlock

image.png

Block的Copy操作

image.png

栈上Block的销毁

image.png

栈上Block的Copy操作

image.png

栈上__block变量的Copy

image.png

__forwarding总结
{
 __block int multiplier = 10;
 _blk = ^int(int num){
  return num*multiplier;
 }
 multiplier = 6;
 [self executeBlock];
}

- (void)executeBlock
{
int result = _blk(4);
NSLog(@"result is %d",result);
}
//  输出结果result是24;
__forwarding存在的意义
  • 不论在任何位置,都可以顺利的访问同一个__block变量。

Block的循环引用

{
  _array = [NSMutableArray arrayWithObject:@"block"];
  _strBlk = ^NSString *(NSString* num){
     return [NSString stringWithFormat:@"halloc_%@",_array[0]];
  };
  _strBlk(@"hello");
}

image.png

解决循环引用

{
  _array = [NSMutableArray arrayWithObject:@"block"];
  __weak NSArray* weakArray = _array;
  _strBlk = ^NSString *(NSString* num){
     return [NSString stringWithFormat:@"halloc_%@",weakArray[0]];
  };
  _strBlk(@"hello");
}

image.png

{
 __block MCBlock* blockSelf = self;
  _blk = ^int (int num){
  // var = 2
     return num * blockSelf*var;
  };
  _blk(3);
}
这段代码的有问题吗?
  • 在MRC下,不会产生循环引用。
  • 在ARC下,会产生循环引用,引起内存泄漏。

image.png

{
 __block MCBlock* blockSelf = self;
  _blk = ^int (int num){
  // var = 2
     int result = num * blockSelf*var;
     blockSelf = nil;
     return result;
  };
  _blk(3);
}
  • 如果block不调用,对象持有一直存在,循环引用一致存在。

Block总结

  • 什么是Block?
  • 为什么Block会产生循环引用?
  • 怎样理解Block截获变量的特性?
  • 你都遇到过哪些循环引用?你又是怎样解决的?