美文网首页iOS 开发 Objective-C
第八篇:Objective-C 知识回顾Block

第八篇:Objective-C 知识回顾Block

作者: 望穿秋水小作坊 | 来源:发表于2019-11-19 15:37 被阅读0次
    Block知识点大纲

    8.1.基本概念

    问题一:什么是 Block?
    • Block 是将函数及其执行上下文封装起来的对象
    • 其本身其实就是函数指针。

    首先看下这段代码,我们要分析 Block 具体干了些啥,可以从 Block 编译后的代码分析。使用 xcrun -sdk iphonesimulator clang -rewrite-objc BDBlock.m 可以生成 BDBlock.cpp 文件,借助这个文件可以查看 Block 内部干了些啥。

    //#import "BDBlock.h"
    @implementation BDBlock
    - (void)myMethod
    {
        int multiplier = 6;
        int(^MyBlock)(int) = ^int(int num) {
            return num * multiplier;
        };
        MyBlock(2);
    }
    @end
    

    截取其中一段代码

    static void _I_BDBlock_myMethod(BDBlock * self, SEL _cmd) {
       int multiplier = 6;
       
       /*
        int(^MyBlock)(int) = ^int(int num) {
            return num * multiplier;
        };
        */
       int(*MyBlock)(int) = ((int (*)(int))&__BDBlock__myMethod_block_impl_0((void *)__BDBlock__myMethod_block_func_0, &__BDBlock__myMethod_block_desc_0_DATA, multiplier));
       
       /*
        MyBlock(2);
        */
       ((int (*)(__block_impl *, int))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock, 2);
    }
    
    问题二:Block 调用的本质是什么?
    • Block 调用的本质就是函数调用

    8.2.Block 对变量的截获

    问题三:请回答 Block 对下面的变量截获都有什么异同?
    请回答 Block 对下面的变量截获都有什么异同?
    - (void)myMethod2
    {
        //基本数据类型的局部变量
        int var = 1;
        //对象型的局部变量
        __unsafe_unretained id unsafe_obj = nil;
        __strong id strong_obj = nil;
        
        //局部静态变量
        static int static_var = 3;
        void(^Block)(void) = ^ {
            NSLog(@"局部变量<基本数据类型> var %d", var);
            
            NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@", unsafe_obj);
            NSLog(@"局部变量<__strong 对象类型> var %@", strong_obj);
            
            NSLog(@"静态局部变量 %d", static_var);
            
            NSLog(@"全局变量 %d", global_var);
            NSLog(@"全局静态变量 %d", global_static_var);
        };
        
        Block();
    }
    

    这段代码被编译的代码可以用命令 xcrun -sdk iphonesimulator clang -rewrite-objc -fobjc-arc BDBlock.m 来生成 cpp 文件查看。

    static void _I_BDBlock_myMethod2(BDBlock * self, SEL _cmd) {
    
        int var = 1;
    
        __attribute__((objc_ownership(none))) id unsafe_obj = __null;
        __attribute__((objc_ownership(strong))) id strong_obj = __null;
    
    
        static int static_var = 3;
        void(*Block)(void) = ((void (*)())&__BDBlock__myMethod2_block_impl_0((void *)__BDBlock__myMethod2_block_func_0, &__BDBlock__myMethod2_block_desc_0_DATA, var, unsafe_obj, strong_obj, &static_var, 570425344));
    
        ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
    }
    

    从编译后的代码可以看出

    • 对于 基本数据的局部变量 Block 采用值传递的截获方式。
    • 对于 对象型的局部变量 Block 采用 连同所有权修饰符 一起截获的方式
    • 对于 局部静态变量 Block 以指针形式截获变量地址
    • 对于 全局变量静态全局变量 Block 不截获

    8.3. __block 修饰符

    问题四:请问下面代码能正常运行吗?是否需要 __block 关键字的协助?
        NSMutableArray *array = [[NSMutableArray alloc] init];
        void(^Block)(void) = ^ {
            [array addObject:@"111"];
        };
        [array addObject:@"222"];
        NSLog(@"%@",array);
        Block();
        NSLog(@"%@",array);
    
    • 上述代码能正常运行
    问题五:请问下面代码能正常运行吗?是否需要 __block 关键字的协助?
        NSMutableArray *array = nil;
        void(^Block)(void) = ^ {
            array = [NSMutableArray new];
        };
        [array addObject:@"222"];
        NSLog(@"%@",array);
        Block();
        NSLog(@"%@",array);
    
    • 上述代码会编译报错,错误信息:Variable is not assignable (missing __block type specifier)
    问题六:请回答什么情况下会使用到 __block 修饰符?
    • 一般情况下,对被截获的变量进行 赋值 操作需添加__block 修饰符
    • 请千万理解 赋值 不等于 使用 ,因此上一个问题只是使用了 array,不需要 __block 关键字的使用,能正常运行。
    问题七:请回答对哪些变量在 Block 代码块里面赋值需要添加 __block 修饰词?
    • 局部基本数据类型
    • 局部对象数据类型
    问题八:请回答对哪些变量在 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 修饰基本数据类型,对基本数据类型做什么事情?
    • __block 修饰基本数据变量,会把变量转变成对象。
    • 并且有一个__forwarding 指针,(如果在栈上创建的)指向基本数据类型对象自己。

    8.4.Block 的内存管理

    问题一:思考 __block 在内存方面有哪几种类型?
    • _NSConcreteGlobalBlock 全局 Block
    • _NSConcreteStackBlock 栈 Block
    • _NSConcreteMallocBlock 堆 Block
    三种 Block 在内存存储位置
    问题二:思考我们在对 Block 进行 copy 操作会产生什么效果?
    对 Block 进行 copy 的结果
    问题三:思考我们在何时需要对 Block 进行 copy 操作?
    栈上的 block 如果不 copy 会随着作用域的结束,而被释放
    问题四:思考既然栈上的__forwarding指向自己,为什么还需要呢?
    • 因为栈上的 Block 经过 copy 之后,__forwarding 会指向对上的 __block 对象。
      -这样就保证了,无论在任何内存位置,都可以顺利的访问同一个 __block 变量。

    8.5.Block 的循环引用问题

    问题一:思考下面的代码存在什么问题?如何解决?
        _mutableArray = [NSMutableArray arrayWithObject:@"block"];
        _blk = ^NSString*(NSString* num) {
            return [NSString stringWithFormat:@"%@", _mutableArray[0]];
        };
        _blk(@"hello");
    
    • 会造成循环引用,并且编译器会提示: Capturing 'self' strongly in this block is likely to lead to a retain cycle
        // 解决方案
        _mutableArray = [NSMutableArray arrayWithObject:@"block"];
        __weak NSArray *weakArray = _mutableArray;
        _blk = ^NSString*(NSString* num) {
            return [NSString stringWithFormat:@"%@", weakArray[0]];
        };
        _blk(@"hello");
    
    • 由于 Block 截获变量的时候,如果是对象类型的,那么会连同对象的所有权 一起截获。我们给他一个 weak 的变量,就会截获 weak 所有权的变量,从而避免了两边强引用而导致的循环引用问题。
    • 这是一种自循环引用,我们通过避免自循环引用的方式解决。
    问题一:思考下面的代码存在什么问题?如何解决?
    __block ViewController* blockSelf = self;
        _blk = ^NSString*(NSString* num) {
            return [NSString stringWithFormat:@"%@", blockSelf.mutableArray[0]];
        };
        _blk(@"hello");
    
    • 上述代码中,self 被 blockSelf 持有,blockSelf 被 _blk 持有,_blk 被 self 持有,这就形成了循环引用中的大环引用。
    • 我们可以通过断环的方式避免循环应用。
    • 但是这种方式有一个弊端,就是 block 如果不被调用,那么循环引用就会一直存在。
        __block ViewController* blockSelf = self;
        _blk = ^NSString*(NSString* num) {
            NSString* temp = [NSString stringWithFormat:@"%@", blockSelf.mutableArray[0]];
            blockSelf = nil;
            return temp;
        };
        _blk(@"hello");
    

    相关文章

      网友评论

        本文标题:第八篇:Objective-C 知识回顾Block

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