假设我们有三个对象:祖父母、父母和孩子。祖父母留住父母,父母留住孩子,孩子留住父母。祖父母释放父母。
在这种情况下会发生什么?
假设我们有三个对象:祖父母、父母和孩子。祖父母留住父母,父母留住孩子,孩子留住父母。祖父母释放父母。
在这种情况下会发生什么?
除非有其他提及父母或孩子,否则他们都会成为孤儿。但是父子之间的保留循环阻止了它们被释放并且它们成为浪费的内存。
孩子永远不应该留下父母。如果有的话,请在子项中使用弱引用来维护对父项的引用。
保留周期是条件当两个对象保持对彼此的引用并被保留时,它会创建一个保留周期,因为两个对象都试图相互保留,因此无法释放。
这里“祖父母”保留“父母”,“父母”保留“孩子”,而“孩子”保留“父母”。这里在父母和孩子之间建立了一个保留循环。释放祖父母后,父母和孩子都成为孤儿,但父母的保留计数不会为零,因为它被孩子保留,因此会导致内存管理问题。
有两种可能的解决方案:
1) 使用指向 parent 的弱指针,即孩子应该使用对 parent 的弱引用,不保留。
2)使用“关闭”方法来打破保留周期。
http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html
在一个简单的情况下,考虑两个对象 A 和 B,其中 A 创建并保留 B。创建 A 时,它会创建 B。当创建 A 的人最终释放它时,A 的保留计数下降到零并被释放。如果 A 的 dealloc 方法在 B 上调用 release,则 B 的保留计数也会下降到零,并且它也会被释放。[这假设没有其他人保留 A 或 B,因为我保持简单。]
但是如果 B 需要对 A 的引用,并且它保留了 A,会发生什么?创建 A 的人可能会释放它。但是由于 B 也保留了 A,所以 A 的保留计数不会变为零。同样,由于 A 保留 B,B 的保留计数也不会归零。两者都不会被释放。即使 B 在自己的 dealloc 中调用 A 的 release 方法也没关系,因为该方法永远不会被调用。
此时您有内存泄漏,因为您没有对 A 或 B 的任何引用,即使它们仍然存在。如果 A 或 B 正在做任何处理器密集型的事情,那么您也可能会将 CPU 时间泄漏给不需要的对象。
在您的情况下,A 是父母,B 是孩子,而创建 A 的人是祖父母。
保留循环是当对象 A 保留对象 B,而对象 B 保留对象 A 时发生的循环。在这种情况下,如果任一对象被释放:
因此,这两个对象将在程序的整个生命周期中一直在内存中徘徊,即使如果一切正常,它们应该被释放。
当祖父母释放父母时,父母仍然活着,而孩子保留父母。
祖父母:John 父母:Ted 孩子:Mary
这是我使用电话进行说明的示例:
John 给 Ted 打电话,想和 Mary 进行电话会议。
泰德对约翰说:“挂断电话,我会拨入玛丽”
泰德让约翰保持等待状态并打电话给玛丽,玛丽立即接听了电话。
玛丽对泰德说:“把我的电话和约翰合并,我不会挂断,直到我结束”
泰德有段时间没有收到约翰的回音,于是离开电话去做别的事情。
约翰去与泰德和玛丽合并电话,然后突然死去。
玛丽挂在约翰的电话上,但永远不会挂断,因为约翰不会回来!
Retain Cycle是当2 个对象保持对彼此的引用并被保留时的条件,它创建一个保留循环,因为两个对象都试图相互保留,因此无法释放。
例子:一个人住在一个部门,一个部门有一个人。
@class Department;
@interface Person:NSObject
@property (strong,nonatomic)Department * department;
@end
@implementation Person
-(void)dealloc{
NSLog(@"dealloc person");
}
@end
@interface Department: NSObject
@property (strong,nonatomic)Person * person;
@end
@implementation Department
-(void)dealloc{
NSLog(@"dealloc Department");
}
@end
然后像这样调用它:
- (void)viewDidLoad {
[super viewDidLoad];
Person * person = [[Person alloc] init];
Department * department = [[Department alloc] init];
person.department = department;
department.person = person;
}
你不会看到 dealloc 日志,这是保留圈。
由于P对象的retainCount为1,释放时,其retainCount变为0,调用其dealloc方法;这反过来又调用 C 对象的释放,其保留计数也变为 0;并调用它的 dealloc 方法。
对象 P 和 C 都将被释放。
当调用 C 对象的 dealloc 方法时,又调用了 GP 对象的释放,但由于 GP 持有的保留计数为 2,保留计数减为 1,并继续挂起。
保留周期是一个死锁条件。保留周期的真实示例:如果两个对象相互持有引用并且没有其他对象被释放。
示例:拉米游戏
当两个对象以这种方式保持彼此的引用时,对象创建循环并被保留。两个对象都试图互相保留,在这种情况下它们是强连接的,不可能释放的称为保留循环