zoukankan      html  css  js  c++  java
  • block的修饰词为什么选用copy

    想必很多开发人员知道一般用copy修饰block,接下来就讲解为什么需要用copy,甚至会讲到其实用strong修饰block也是可以的

    在 Objective-C 语言中,一共有 3 种类型的 block:

    1. _NSConcreteGlobalBlock 全局的静态 block,没有访问外部局部变量(基本数据、OC对象)、成员属性变量或只用到全局变量、静态变量(局部或者全局静态变量)
    2. _NSConcreteStackBlock 保存在栈中的 block,只用到外部局部变量(基本数据、OC对象)、成员属性变量,且没有强指针引用的block。当函数返回时会被销毁。
    3. _NSConcreteMallocBlock 保存在堆中的 block,有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制。

    新建一个项目,分别在ARC环境和MRC环境测试一遍

    把测试文件改成MRC环境的方法:

    把完整的测试代码显示如下:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @property (nonatomic,copy)void(^demoBolck)();
    
     @property (nonatomic,strong)void(^demoBolck1)();
    
    @end
    
    @implementation ViewController
    
    int b=8;//全局变量
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        void (^demoBolck)() = ^{
    
            NSLog(@"indemoBolck");
    
        }; 
    
        NSLog(@"demoBolck %@",demoBolck);    //<__NSGlobalBlock__: 0x1085af0e0>  无论ARC还是MRC下,因不访问外部局部变量(基本数据、OC对象)、成员属性变量或只用到全局变量、静态变量(局部或者全局静态变量),NSGlobalBlock表示在全局区
    
        void (^demoBolck4)() = ^{
    
            NSLog(@"indemoBolck4  %d",b);
    
        };
    
        NSLog(@"demoBolck4 %@",demoBolck4);    //<__NSGlobalBlock__: 0x10150b120>  全局区
    
        __block int a = 6;   //block内部引用a,并修改其值,需要用block修饰,不然可以不用
    
        void (^demoBolck2)() = ^{
    
            NSLog(@"indemoBolck2 %d",a++);
    
        };
    
        demoBolck2();
    
        NSLog(@"demoBolck2 %@,%d",demoBolck2,a);   //<__NSMallocBlock__: 0x600000056c50> ARC下堆区,在ARC模式下,系统也会默认对Block进行copy操作,Block的内存地址这时候便显示在堆区; <__NSStackBlock__: 0x7fff5d0ada28>MRC下在栈区
    
        NSLog(@"demoBolck2.Copy %@",[demoBolck2 copy]);    //<__NSMallocBlock__: 0x600000056c50>copy操作不管MRC或者ARC都在堆区,只是在MRC下进行copy会改变地址
    
        self.demoBolck = demoBolck2;
    
        NSLog(@"self.demoBolck %@",self.demoBolck);//堆区<__NSMallocBlock__: 0x608000052630>
    
        self.demoBolck1 = demoBolck2;
    
        self.demoBolck1();     //demoBolck2  7   能执行无问题
    
        NSLog(@"self.demoBolck1 %@",self.demoBolck1);     //<__NSMallocBlock__: 0x600000056c50>  strong修饰ARC和MRC都并没有问题, 但是assign和retain在MRC环境下是还是在栈区的,会有问题
    
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
        //注意:MRC环境下:demoBolck1用assign,retain修饰,栈区:<__NSStackBlock__: 0x7fff50915a50>,提前释放了所以运行到下面语句程序会崩溃。ARC环境下:用copy,strong, assign, retain修饰是可以正常打印出结果的。 无论什么环境,用copy,strong修饰是可以正常打印出结果的
    
        self.demoBolck1();
    
    }
    
    @end

    总结:

    • block内部没有调用外部局部变量时存放在全局区(ARC和MRC下均是)
    • block使用了外部局部变量,这种情况也正是我们平时所常用的方式。MRC:Block的内存地址显示在栈区,栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.所以在使用Block属性时使用copy修饰。但是ARC中的Block都会在堆上的,系统会默认对Block进行copy操作
    • 用copy,strong修饰block在ARC和MRC都是可以的,都是在堆区
    补充:一个block要使用self,会处理成在外部声明一个weak变量指向self,然而为何有时会出现在block里又声明一个strong变量指向weakSelf?
    原因:block会把写在block里的变量copy一份,如果直接在block里使用self,(self对变量默认是强引用)self对block持有,block对self持有,导致循环引用,所以这里需要声明一个弱引用weakSelf,让block引用weakSelf,打破循环引用。
    而这样会导致另外一个问题,因为weakSelf是对self的弱引用,如果这个时候控制器pop或者其他的方式引用计数为0,就会释放,如果这个block是异步调用而且调用的时候self已经释放了,这个时候weakSelf已就变成了nil。
    当控制器(也可以是其他的控件)pop回来之后(或者一些其他的原因导致释放),网络请求完成,如果这个时候需要控制器做出反映,需要strongSelf再对weakSelf强引用一下。
    但是,你可能会疑问,strongSelf对weakSelf强引用,weakSelf对self弱引用,最终不也是对self进行了强引用,会导致循环引用吗。不会的,因为strongSelf是在block里面声明的一个指针,当block执行完毕后,strongSelf会释放,这个时候将不再强引用weakSelf,所以self会正确的释放。
  • 相关阅读:
    KMP算法与字符串匹配问题
    贪婪算法(贪心算法)
    普里姆算法(Prim)与最小生成树问题
    克鲁斯卡尔算法(Kruskal算法)与最小生成树问题
    Dijkstra算法与最短路径问题
    SpringCloud(十一)使用actuator和dashborad、turbine对微服务进行监控
    博客美化——页面白天黑夜切换
    Spring5学习笔记——day01
    Mybatis学习笔记——day06
    Mybatis学习笔记——day05
  • 原文地址:https://www.cnblogs.com/jingxin1992/p/7145596.html
Copyright © 2011-2022 走看看