iOS Block 详解

作者: 年轻就要活出样 | 来源:发表于2019-08-18 23:39 被阅读0次

    xcrun -sdk iphonesimulator clang -rewrite-objc ViewController.m

    注意:ARC环境下编译器,会自动帮我们添加copy操作。

    1、block有三种:

    NSStackBlock存储于栈区
    NSGlobalBlock存储于程序数据区
    NSMallocBlock存储于堆区

    NSGlobalBlock 静态区block,这是一种特殊的bloclk,因为不引用外部变量而存在。另外,作为静态区的对象,它的释放是有操作系统控制的,这一点我们最后再聊。
    NSStackBlock 栈区block,位于内存的栈区,一般作为函数的参数出现。 需要引入外部参数,arc环境下不能赋值,否则编译器会自动添加copy操作,变成堆block
    NSMallocBlock 堆区block,位于内存的堆区,一般作为对象的property出现。

    ARC环境下

       void (^block)(void) = ^{
            NSLog(@"hello block");
        };
        NSLog(@"block = %@",block); // 全局静态block: __NSGlobalBlock__   地址段以0x1开头
        int a = 10;
        void (^block1)(void) = ^{
            NSLog(@"hello bloc %d ",a);
        };
        
        NSLog(@"block = %@",block1); //  __NSMallocBlock__ 堆block   地址段以0x6开头
    
        NSLog(@"%@",^{
            NSLog(@"hello bloc %d",a);    //  __NSStackBlock__  栈block   地址段以0x7开头
        });
    
    log日志:
     2019-08-18 23:05:59.207457+0800 block[3171:134706] block = <__NSGlobalBlock__: 0x101b49ac0>
    2019-08-18 23:05:59.207626+0800 block[3171:134706] block = <__NSMallocBlock__: 0x6000009c9da0>
    2019-08-18 23:05:59.207729+0800 block[3171:134706] <__NSStackBlock__: 0x7ffeee16d318>
    
        int a = 10;
        [self uuuuuu:^{
            NSLog(@"hello bloc %d ",a);
        }];
    - (void)uuuuuu:(void(^)(void))clock{
        NSLog(@"%@",clock);
        int a = 10;
        NSLog(@"%@",^{
            NSLog(@"hello bloc %d",a);    //  __NSStackBlock__  栈block   地址段以0x7开头
        });
    }
    log: 2019-08-18 23:19:27.630741+0800 block[3335:145771] <__NSStackBlock__: 0x7ffee0e9d388>
    2019-08-18 23:19:27.630809+0800 block[3335:145771] <__NSStackBlock__: 0x7ffee0e9d330>
    

    __block 修饰符

    屏幕快照 2019-08-18 下午11.34.27.png
    屏幕快照 2019-08-18 下午11.35.59.png
    这里是使用操作不需要添加__block修饰符
    屏幕快照 2019-08-18 下午11.38.12.png
    这里是 赋值 操作不需要添加__block修饰符
    屏幕快照 2019-08-20 下午10.44.17.png
    屏幕快照 2019-08-20 下午10.48.38.png

    在栈上创建的block,注意:block在栈上创建

    屏幕快照 2019-08-20 下午10.47.12.png

    对栈block的进行copy操作,发生了什么

    屏幕快照 2019-08-20 下午10.53.04.png

    总结 在栈上对__block变量进行修改,如果此时我们队栈上的__block变量已经做过copy操作之后,实际上我们修改的不是栈上的__block变量的值;而是通过栈上的__block变量内部的__forwording指针,找到堆上面的__blcok变量,然后对堆上的__block变量的值进行修改。
    如果对堆上面的__block变量进行修改,是通过自身(堆)上面的__block变量内部的__forwording指针进行修改。

    问题 (3)在block内如何修改block外部变量?

    注释:我们都知道:Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
    __block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。

    2、Block劫持变量特性

    • 对于基本数据类型的局部变量截获其值
    • 对于对象类型的局部变量 连同所有权修饰符一起截获
    • 对于局部静态变量指针的形式进行截获
    • 对于全局变量、静态全局变量不截获

    如下代码编译之后源码

    // 全局变量
    int global_a = 30;
    // 静态全局变量
    static int static_global_a = 40;
    - (void)method{
        // 局部变量
        int a               = 10;
        // 局部对象
        NSObject *obj_a     = [NSObject new];
        // 局部静态变量
        static int static_a = 20;
        
        void (^Block)(void) = ^(void){
            NSLog(@"%d",a);
            NSLog(@"%@",obj_a);
            NSLog(@"%d",static_a);
            NSLog(@"%d",global_a);
            NSLog(@"%d",static_global_a);
        };
        Block();
    }
    
    // 全局变量
    int global_a = 30;
    // 静态全局变量
    static int static_global_a = 40;
    
    struct __MCBlock__method_block_impl_0 {
      struct __block_impl impl;
      struct __MCBlock__method_block_desc_0* Desc;
    
      // 截获局部变量的值
      int a;
      // 连同对象所有权一起截获
      NSObject *obj_a;
      // 以指针的形式截获静态局部变量
      int *static_a;
      // 全局变量、静态全局变量不对其截获
        
      __MCBlock__method_block_impl_0(void *fp, struct __MCBlock__method_block_desc_0 *desc, int _a, NSObject *_obj_a, int *_static_a, int flags=0) : a(_a), obj_a(_obj_a), static_a(_static_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    请看如下例子1、:

        int a = 10;
        void (^block)(void) = ^{
            a  = 20;
            NSLog(@"%d",a);
        };
    

    请问此时编译之后的结果:
    此时会抛出:Variable is not assignable (missing __block type specifier)
    因为block对基本数据类型局部变量是值的传递,这时候我们在block里面去修改外部变量的值,是不允许的。举例方法中的参数,我们可以在方法内部打印其值,但是我们不能够去修改参数的值

    请看如下例子2、:

       // 局部变量
      int a = 10;
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };
      a   = 20;
      block();
    请问此时打印多少?   打印    10;
    

    请看如下例子3、:

        // __block 修饰的局部变量
      __block int a = 10;
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };
        a   = 20;
        block();
        a   = 30;
    请问此时打印多少?   打印    20;
    

    请看如下例子3、:

        // 局部静态变量
        static int a = 10;
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };
        a   = 20;
        block();
        a   = 30;
    请问此时打印多少?   打印    20;
    

    请看如下例子4、:

        // 局部全局变量
        static int a = 10;
    {
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };
        a   = 20;
        block();
        a   = 30;
    }
    请问此时打印多少?   打印    20;
    

    请看如下例子5、:

        // 静态全局变量
        static int a = 10;
    {
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };
        a   = 20;
        block();
        a   = 30;
    }
    请问此时打印多少?   打印    20;
    

    请看如下例子6、:

        // 全局变量
       int a = 10;
    {
        void (^block)(void) = ^{
            NSLog(@"%d",a);
        };
        a   = 20;
        block();
        a   = 30;
    }
    请问此时打印多少?   打印    20;
    

    请看如下例子7、:

    @interface ViewController ()
    @property (nonatomic, assign)    NSInteger           a;
    end
    @implementation ViewController
    - (void)viewDidLoad {
     void (^block)(void) = ^{
            NSLog(@"%ld",(long)_a);
        };
        _a   = 20;
        block();
        _a   = 30;
    }
    @end
    请问此时打印多少?   打印    20;
    

    相关文章

      网友评论

        本文标题:iOS Block 详解

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