Block你应该知道的事

2019-07-08  本文已影响0人  三分慢先森

Block 是将函数及其执行上下文封装起来的对象(结构体)。Block的调用实质上就是函数的调用。
----它的内存是分配在栈上的,当赋值的成员变量调用block时,可能会因为栈上对应的函数退出后在内存中被释放,而导致内存崩溃。为了防止这个问题,所以都是把block拷贝到堆中使用。
(在ARC下,使用copy和strong是一样的,因为block的retain是用copy来实现的,不过一般习惯性使用copy)

一、截获变量
二、__block修饰符
三、避免block循环引用

一. block对不同变量的截获
1>.对于基本数据类型的局部变量只截获其值。
2>.对于对象类型的局部变量连同所有权修饰符一起截获(强引用)。
3>.以指针形式截获局部静态变量。
4>.不截获全局变量、静态全局变量,直接进行使用。

1.普通局部变量

int age = 10;
void(^localBlock)(void) = ^{
  NSLog(@"age = %d", age);
};
age = 18;
localBlock();            
//输出age = 10;在age改变为18之前,localBlock已经截获age的值(10)保存到localBlock结构体中

二. __block修饰的变量
== 对于用 __block 修饰的变量(实际变成了一个结构体),在截获时,block结构体中保存了被修饰变量的地址和值,相当于进行指针拷贝。在block中可以修改这样的变量的值。

__block int age = 10;
void(^localBlock)(void) = ^{
  NSLog(@"age = %d", ++age);
};
age = 18;
localBlock();
/*
输出age = 19;另外解释下当去除__block修饰时,会报错not assignable,
原因是局部变量保存在栈中,而block保存在堆中,不在一块内存空间里,而你block只知道我的值不知道我的家在哪里,当然不能修改我。
*/

一般情况,对截获的对象类型局部变量进行赋值操作需添加__block修饰符。赋值不等于使用(赋值是初始化,例如向已存在的数组中添加数据就不属于赋值而只是使用,就不必使用__block修饰符)。

__block NSMutableArray *mArr = [[NSMutableArray alloc] init];
void(^localBlock)(void) = ^{
   //使用mArr 不需要 __block修饰
  [mArr addObject:@"1212"];   
   //给mArr赋值 需要 __block修饰
  mArr = [[NSMutableArray alloc] initWithObjects:@"222", nil];
};
localBlock();

三. 解决block的循环引用(让你从此告别只会weakSelf的青葱岁月)
1.weak->strong

self.name = @"James";
__weak typeof(self) weakSelf = self;
void(^localBlock)(void) = ^(){
  __strong typeof(self) strongself = weakSelf;
  //此处延迟两秒执行,模拟数据请求或者耗时操作
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
      NSLog(@"%@",strongself.name);
  });
};
localBlock();

strongSelf使用解释:
----试想一种情况,当block在执行耗时操作时,当前控制器被推出,而此时weakSelf由于在弱引用表中会马上被释放,block对self的弱引用没有引用计数加持,就导致block作用域内的self也会立刻置为nil。
----而如果在block的作用域中设置一个临时变量对weakSelf进行强引用,使block拥有对self的引用计数,这样即使在控制器被推出后,也因为block强持有了控制器而不会马上被释放。
---而strongSelf由于是block作用域内的局部变量,也会在block执行完成后被自动释放池解决掉,此时控制器最终释放,这样既能避免循环引用也能够保证block代码块的完整执行。

2.__block修饰

self.name = @"James";
__block CurrentVC *vc = self;
void(^localBlock)(void) = ^(){
  //此处延迟两秒执行,模拟数据请求或者耗时操作
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
      NSLog(@"%@",vc.name);
      //__block修饰的对象会被block强引用,需要手动解环
      /*
        此处还要解释的一点是,虽然上文说__block修饰相当于做了指针拷贝,
        但实际情况 vc是将原self的地址和内容作为两个引用保存在一个结构体内,
        所以vc置nil并不影响外部的self
      */
      vc = nil;
  });
};
localBlock();
  1. self作为参数
void(^localBlock)(CurrentVC *vc) = ^(CurrentVC *vc){
      NSLog(@"%@",vc.name);
};
localBlock(self);

如果有疏漏的地方,也希望各位coder不吝指教

上一篇下一篇

猜你喜欢

热点阅读