简体   繁体   English

深入了解保留循环

[英]Understanding retain cycle in depth

Lets say we have three objects: a grandparent, parent and child.假设我们有三个对象:祖父母、父母和孩子。 The grandparent retains the parent, the parent retains the child and the child retains the parent.祖父母保留父母,父母保留孩子,孩子保留父母。 The grandparent releases the parent.祖父母释放父母。

What will happen in this case ?在这种情况下会发生什么?

Unless there is some other reference to the parent or child, they both become orphaned.除非有其他对父母或孩子的引用,否则他们都会成为孤儿。 But the retain cycle between the parent and child prevent either from being released and they become wasted memory.但是父级和子级之间的保留循环可以防止任何一个被释放并且它们成为浪费的内存。

A child should never retain a parent.孩子永远不应该留住父母。 If anything, use a weak reference in the child to maintain a reference to the parent.如果有的话,请在子级中使用弱引用来维护对父级的引用。

Retain Cycle is the condition When 2 objects keep a reference to each other and are retained, it creates a retain cycle since both objects try to retain each other, making it impossible to release. Retain Cycle 是一个条件,当两个对象相互保持引用并被保留时,它会创建一个保留循环,因为两个对象都试图相互保留,从而无法释放。

Here The "Grandparent" retains the "parent" and "parent" retains the "child" where as "child" retains the "parent".. Here a retain cycle is established between parent and child.这里“祖父母”保留“父母”,“父母”保留“孩子”,而“孩子”保留“父母”。在这里,父母和孩子之间建立了一个保留循环。 After releasing the Grandparent both the parent and child become orphaned but the retain count of parent will not be zero as it is being retained by the child and hence causes a memory management issue.释放祖父母后,父母和孩子都成为孤儿,但父母的保留计数不会为零,因为它被孩子保留,因此会导致内存管理问题。

There are two possible solutions:有两种可能的解决方案:

1) Use weak pointer to parent , ie a child should be using weak reference to parent, which is not retained. 1) 使用指向 parent 的弱指针,即一个 child 应该使用对 parent 的弱引用,这不会被保留。

2) Use "close" methods to break retain cycles. 2) 使用“close”方法来中断保留循环。

http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

In a simple case, consider two objects A and B where A creates and retains B. When A is created, it creates B. When whoever created A finally releases it, A's retain count drops to zero and it gets deallocated.在一个简单的例子中,考虑两个对象 A 和 B,其中 A 创建并保留 B。当 A 被创建时,它会创建 B。当创建 A 的人最终释放它时,A 的保留计数下降到零并被释放。 If A's dealloc method calls release on B, B's retain count also drops to zero and it also gets deallocated.如果 A 的 dealloc 方法在 B 上调用 release,B 的保留计数也会下降到零,并且它也被释放。 [This assumes that nobody else has retained A or B, because I'm keeping things simple.] [这假设没有其他人保留 A 或 B,因为我保持简单。]

But what happens if B needs a reference back to A, and it retains A?但是如果 B 需要一个对 A 的引用,并且它保留了 A,会发生什么? Whoever created A might release it.谁创建了 A 可能会释放它。 But since B has also retained A, A's retain count won't go to zero.但由于 B 也保留了 A,因此 A 的保留计数不会为零。 Likewise, since A retains B, B's retain count also won't go to zero.同样,由于 A 保留了 B,B 的保留计数也不会为零。 Neither will be deallocated.两者都不会被解除分配。 Even if B calls A's release method in its own dealloc it doesn't matter, because that method is never going to be called.即使 B 在自己的 dealloc 中调用 A 的 release 方法也没关系,因为该方法永远不会被调用。

At this point you have a memory leak, because you don't have any reference to A or B even though they both still exist.此时您有内存泄漏,因为您没有对 A 或 B 的任何引用,即使它们仍然存在。 If A or B is doing anything processor intensive then you might also be leaking CPU time to unwanted objects.如果 A 或 B 正在做任何处理器密集型的事情,那么您也可能将 CPU 时间泄漏给不需要的对象。

In your case A is parent and B is child and whosoever created A is grandparent.在您的情况下,A 是父母,B 是孩子,而创建 A 的人是祖父母。

A retain cycle is a loop that happens when Object A retains Object B, and Object B retains Object A. In that situation, if either object is released:保留循环是当对象 A 保留对象 B,对象 B 保留对象 A 时发生的循环。在这种情况下,如果释放任一对象:

  • Object A won't be deallocated because Object B holds a reference to it (retain count > 0).对象 A 不会被释放,因为对象 B 持有对它的引用(保留计数 > 0)。
  • Object B won't ever be deallocated as long as Object A has a reference to it (retain count > 0).只要对象 A 有对它的引用(保留计数 > 0),对象 B 就永远不会被释放。
  • But Object A will never be deallocated because Object B holds a reference to it (retain count > 0).但是对象 A 永远不会被释放,因为对象 B 持有对它的引用(保留计数 > 0)。
  • till infinity直到无限

Thus, those two objects will just hang around in memory for the life of the program even though they should, if everything were working properly, be deallocated.因此,这两个对象将在程序的整个生命周期中停留在内存中,即使它们应该(如果一切正常)被释放。

当祖父母释放父母时,父母仍然活着,因为孩子保留父母。

Grandparent : John Parent : Ted Child : Mary祖父母:约翰父母:泰德孩子:玛丽

Here is my example using a telephone call for illustration:这是我使用电话进行说明的示例:

  • John calls Ted and wants to do a conference call with Mary.约翰打电话给泰德,想和玛丽开个电话会议。

  • Ted Says to John: "Hang on the line, and I will dial in Mary"泰德对约翰说:“挂断电话,我会拨通玛丽”

  • Ted leaves John on hold and calls Mary who promptly answers the phone.泰德让约翰保持等待,并给玛丽打电话,玛丽立即接听了电话。

  • Mary says to Ted: "Merge my call in with John and I WILL NOT hang up until I'm through"玛丽对泰德说:“把我和约翰的电话合并,我不会挂断电话,直到我完成为止”

  • Ted, having not heard back from John in a while, leaves the call to do something else. Ted 有一段时间没有收到 John 的回音,离开电话去做其他事情。

  • John goes to merge the calls with Ted and Mary and then suddenly dies.约翰去与泰德和玛丽合并电话,然后突然死了。

  • Mary is stuck on the line to John but will never hang up cause John ain't coming back!玛丽一直挂在约翰的电话线上,但永远不会挂断,因为约翰不会回来!

Retain Cycle is the condition when 2 objects keep a reference to each other and are retained , it creates a retain cycle since both objects try to retain each other, making it impossible to release. Retain Cycle两个对象相互保持引用并被保留的条件它创建了一个保留循环,因为两个对象都试图相互保留,从而无法释放。


Example: A person lives in a department, a department has one person.例子:一个人住在一个​​部门,一个部门有一个人。

@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

Then call it like this:然后像这样调用它:

- (void)viewDidLoad {
    [super viewDidLoad];
    Person * person = [[Person alloc] init];
    Department * department = [[Department alloc] init];
    person.department = department;
    department.person = person;
}

You will not see dealloc log, this is the retain circle.您不会看到 dealloc 日志,这是保留圈。

Since the P object has retainCount of 1, when it is released, its retainCount goes to 0, and its dealloc method is called;由于P对象的retainCount为1,释放时其retainCount变为0,调用dealloc方法; This in turn calls release on C object, whose retain count also goes to 0;这反过来调用释放 C 对象,其保留计数也变为 0; and its dealloc method is called.并调用它的 dealloc 方法。

Both objects P and C will get freed.对象 P 和 C 都将被释放。

When C object's dealloc method is called, in turn GP object's release is called, but since GP holds a retain count of 2, the retain count is decremented to 1, and it continues to hang around.当调用C对象的dealloc方法时,依次调用GP对象的release,但由于GP持有retain count为2,retain count递减为1,继续挂起。

Retain cycle is a deadlock condition.保留循环是一种死锁条件。 Real Life Example of Retain Cycle: If two object hold a reference each other and no other object is released. Retain Cycle 的真实例子:如果两个对象互相持有一个引用并且没有其他对象被释放。

Example: Rummy Game示例:拉米游戏

When two objects keep references of each other in such a way the objects create cycle and are retained.当两个对象以这种方式保持彼此的引用时,对象会创建循环并被保留。 Both objects try to retain each other, in this case they are strongly connected with each other and impossible to release is called retain cycle两个对象都试图互相保留,在这种情况下它们彼此之间有很强的联系并且无法释放,称为保留循环

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM