想必很多开发人员知道一般用copy修饰block,接下来就讲解为什么需要用copy,甚至会讲到其实用strong修饰block也是可以的
在 Objective-C 语言中,一共有 3 种类型的 block:
- _NSConcreteGlobalBlock 全局的静态 block,不会访问外部局部变量(显然包括无外部变量或者全局变量)。
- _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
- _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都是可以的,都是在堆区