iOS面试--Block

作者: iOS心安 | 来源:发表于2021-07-13 15:18 被阅读0次

    Block实质
    截图变量
    __block修饰符
    Block的内存管理
    Block的循环引用


    截图变量

    先看第一个问题

    // 全局变量
    int global_var = 4;
    // 静态全局变量
    static int static_global_var = 5;
    - (void)method
    {
         int multiplier = 6;
        int(^Block)(int) = ^int(int num)
        {
            return num * multiplier;
        };
        multiplier = 4;
        NSLog(@"result is %d", Block(2));
    }
    输出是什么? 
     如果 将 int multiplier 改为静态变量 static  int multiplier = 6, 结果又是什么?
    

    带着问题往下看,有这几种类型的变量

    局部变量 – 基本数据类型 , 对象类型 (对于基本数据类型的局部变量截获其值,对于对象类型的局部变量连同 所有权修饰符 一起截获)

    静态局部变量 ( 以指针形式截获局部静态变量 )

    全局变量 (不截获全局变量)

    静态全局变量 (不截获静态全局变量)
    从这段代码来深入了解一下

    int global_var = 4;
    static int static_global_var = 5;
    -(void)method1
    {
        int var = 1;
        __unsafe_unretained id unsafe_obj = nil;
        __strong id strong_obj = nil;
        static int static_var = 3 ;
        void(^block)(void) = ^{
            
            NSLog(@"局部变量<基本数据类型> var %@",var);
            NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@",unsafe_obj);
            NSLog(@"局部变量< __strong 对象类型> var %@",strong_obj);
            NSLog(@"静态变量 %d",static_var);
            NSLog(@"全局变量 %d",global_var);
            NSLog(@"静态全局变量 %d",global_var);
        }
        
    }
    

    回到问题

    int multiplier  = 6 ,block(2)输出的是 12,因为block执行的时候截获的是 6
    static int multiplier  = 6 ,block(2) 输出的是8,因为截获以指针形式截获,所以获取到的  multiplier 是最新的值 4
    

    __block修饰符
    什么情况下需要用到 __block修饰符 呢?
    对被截获变量进行赋值操作的时候 (区分 赋值 和使用)

    看一些笔试题

     NSMutableArray *array = [NSMutableArray array];
        void(^block)(void) = ^{
            [array addObject:@123];
        };
        Block();
    这里  对 array 只是一个使用,而不是赋值,所以不需要 _ _block 进行修饰
      NSMutableArray *array = nil;
        void(^block)(void) = ^{
                array = [NSMutableArray array];
        };
        Block();
    这里就需要在array的声明处添加__block修饰符,不然编译器会报错
    

    总结下,对变量进行赋值的时候,下面这些不需要__block修饰符

    静态局部变量
    全局变量
    静态全局变量

    {
        __Block int multiplier = 6;
        int(^Block)(int) = ^int(int num)
        {
            return num * multiplier;
        };
        multiplier = 4;
        NSLog(@"result is %d", Block(2));
    }
    //这里的结果就是 8 了
    

    Block的循环引用

    下面这段代码就会造成循环引用

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

    self 持有 Block,而 Block 里有成员变量 array, 持有 self,所以就造成了循环引用,怎么解决呢?

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

    为什么用_ weak 修饰符解决循环引用? 这个其实在截获变量里有讲过,截获对象的时候会连同修饰符一起截获,在外部定义的如果是 weak 修饰符,在 Block 里所产生的结构体里面所持有的成员变量也是 _weak 类型

    再看一段代码,这样写有什么问题?

    __block MCBlock*blockSelf = self;
        _blk = ^int(int num){
              //var = 2
                  return num * blockSelf.var ;
        };
            _blk(3);
    

    这样在 ARC 模式下是会产生循环引用,引起内存泄漏的

    __block修饰后的指向是原来的对象,会造成循环引用
    怎么解决呢,首先想到的当然是断开其中一个环

    __block MCBlock*blockSelf = self;
        _blk = ^int(int num){
              //var = 2
          int result = num * blockSelf.var;
          blockSelf = nil;
           return result;
        };
            _blk(3);
    

    在调用完 blockSelf 后将它置为nil,断开其中的一个环,就可以让内存得到释放和销毁
    但是这样会有一个弊端,如果长期不调用这个block,这个循环引用的环就会一直存在


    更多面试资料

    Block 面试总结

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


    转载

    相关文章

      网友评论

        本文标题:iOS面试--Block

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