美文网首页
Block你应该知道的事

Block你应该知道的事

作者: 三分慢先森 | 来源:发表于2019-07-08 21:40 被阅读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不吝指教

    相关文章

      网友评论

          本文标题:Block你应该知道的事

          本文链接:https://www.haomeiwen.com/subject/stiphctx.html