block的修饰词为什么选用copy

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

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

  1. _NSConcreteGlobalBlock 全局的静态 block,不会访问外部局部变量(显然包括无外部变量或者全局变量)。
  2. _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
  3. _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。

新建一个项目,分别在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下,因不访问外部局部(包括无外部变量或者只有全局变量),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会正确的释放。

 

转载于:https://www.cnblogs.com/jingxin1992/p/7145596.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值