经常听说block的循环应用,就是说的A引用B ,B又引用A,这样的交叉引用,导致AB都不能释放,后果就是,内存会增加,甚至导致程序崩溃。
下面通过代码来看产生的原因
首先定义一个Friend的类
1.1 Friend.h文件
import <Foundation/Foundation.h>
@interface Friend : NSObject
@property(nonatomic ,copy) void(^testBlock)();
-(void)run;
@end
1.2 Friend.m文件
import "Friend.h"
@implementation Friend
添加了一个run的测试的方法
-(void)run {
NSLog(@" person run");
}
-(void)dealloc{
NSLog(@"friend被释放了");
}
2.0 在viecontroller.m文件中
3.1
Friend p = [[Friend alloc]init]; 这句代码的解释是
当实例化一个对象的时候 这个对象的p 指针放在栈内存中,
对象p存放在堆内存中。
3.2 @property(nonatomic ,copy) void(^testBlock)();
当我们声明一个block的成员变量的时候,这个block成员变量存放在栈中,
但我们调用这个block用的的是copy属性 (如果没有对block用copy,block存放 在栈内存控件中),这个block就会转移到堆中,这样就会对在block中的对象产生强引用。 那么执行这句代码的时候。堆空间中的block会指向在p这个对象中用的block,这样block就会对对象p产生一个强引用,但是testblock 也是P的成员属性。 所以循环引用就产生了。
p.testBlock =^{
[ p run];
};
-
(void)viewDidLoad {
[super viewDidLoad];Friend *p = [[Friend alloc]init];
p.testBlock =^{
这个时候系统就会产生警告,产生了循环引用
[ p run];
};
}
3 下面来讨论产生循环引用的原因 - 1、默认情况下,block 是放在栈里面的
- 2、一旦blcok进行了copy操作,block的内存就会被放在堆里面
- 3、堆立面的block(被copy过的block)有以下现象
- 1> block内部如果通过外面声明的强引用来使用,那么block内部会自动产生一个强引用指向所使用的对象。
- 2> block内部如果通过外面声明的弱引用来使用,那么block内部会自动产生一个弱引用指向所使用的对象。
- 什么是copy:
- copy同样是一个实例方法,只能由对象调用,返回一个新的对象,它的作用是复制一个对象到一块新的内存空间上,旧内存空间的引用计数不会变化,新的内存空间的引用计数从0增加到1,也就是说,虽然内容一样,但实质上是两块内存,相当于克隆,一个变成两个。其中copy又分为浅拷贝、深拷贝和真正的深拷贝,浅拷贝只是拷贝地址与retain等同;深拷贝是拷贝内容,会新开辟新内存,与retain不一样;真正的深拷贝是对于容器类来说的,如数组类、字典类和集合类(包括可变和不可变),假设有一个数组类对象,普通的深拷贝会开辟一块新内存存放这个对象,但这个数组对象里面的各个元素的地址却没有改变也就是说数组元素只是进行了retain或者浅拷贝而已,并没有创建新的内存空间,而真正的深拷贝,不但数组对象本身进行了深拷贝,连数组元素都进行了深拷贝,即为各个数组元素开辟了新的内存空间。
二 Block中的循环应用的处理方法
-
(void)viewDidLoad {
[super viewDidLoad];Friend *p = [[Friend alloc]init];
下面这两句代码任选一句就可以了,但是官方推荐选用第一句__weak typeof (p) weakp = p;
__unsafe_unretained typeof (p) weaka = p ;
p.testBlock =^{
[weakp run];
};
}
还有一篇文章很好: https://www.jianshu.com/p/7a9c8c8e53a0