2009-04-26 67 views
10

有兩個對象A和B.A創建B並保留它。 B有一個指向A的實例變量,保留它。所以兩個都保留彼此。有人說,這種強大的聯繫不能再一次被打破。保留週期:爲什麼這麼糟糕?

但是,真的是這樣嗎?

如果B會釋放A,那麼A可以輕鬆釋放B,因此B將被釋放。一旦它是其他所有者(我想必須有人)釋放它,它就會被釋放。

或者,這個問題只適用於A沒有創建B的情況,但只是通過將它保留在實例變量中而強制引用它?我仍然不明白爲什麼這個連接不能再被打破。

回答

15

週期並不糟糕,但它們經常被避免,因爲它們可以使得確保沒有內存泄漏變得棘手。特別是當對象被「引用計數」時發生泄漏。在使用引用計數的語言或系統中,對象會跟蹤指向它的引用數量。每次刪除引用時,計數都會減少,當計數變爲零時,就沒有引用,因此可以刪除該對象。

這通常照顧好自己,沒有任何仔細的思考就可以正常工作。如果你有一組沒有循環的對象,並且你刪除了對根對象的引用,那麼它將被刪除,這意味着它必須將它所擁有的對象的引用放棄,被引用的對象將有它們的引用計數去零。他們將被刪除,級聯會導致所有對象被刪除。

但是...如果你有一個週期,這個級聯不起作用。你可能有一組對象,你不再需要它們,所以你放棄了對這些對象的唯一引用,但是因爲有一個循環,對象互相引用。這意味着它們的引用計數永遠不會變爲零,並且它們不會被刪除。這是內存泄漏。

很明顯,你可以做一些仔細的管理和打破循環,然後再把你的引用放到一組你不想要的對象上。但是......就像我剛纔所說的那樣,這需要仔細的管理。錯誤很容易。這是發生內存泄漏的主要原因之一。

爲了避免泄漏的風險以及當您不再需要一組對象時正確分解循環的棘手工作,程序員通常會嘗試避免循環。對於許多程序員而言,對於那些沒有人理解整個系統的大型項目而言,這變得更加重要。如果有周期,程序員必須小心並花費很長時間研究其他代碼以避免週期。

使用垃圾收集器(例如C#)的某些語言可以刪除一組不再需要的對象,即使該組包含循環。

+2

Objective-C的垃圾回收器(啓用時)也可以刪除retain-loop組(幾乎所有垃圾回收器都可以),但這與iPhone中不相關,其中Objective-C垃圾回收不受支持。 – 2009-04-26 23:17:54

2

問題是這樣的:A指向並保留B,B指向並保留A.當沒有其他引用A或B時,將無法釋放它們,因爲您的應用程序不在這一點上有任何提及他們的信息。這被稱爲參考週期,它是任何參考計數系統中常見的一種內存泄漏。在大多數高級語言中解決這個問題的方式是使用垃圾收集而不是引用計數。

+0

謝謝。但爲什麼不應該有其他的A或B?在這種情況下,我可以有兩個對象,我的應用程序沒有提及它們? – Thanks 2009-04-26 18:58:20

+0

如果您對A或B有其他參考,則沒有問題。只有當你失去這些引用(比如如果他們超出範圍),這是一個問題。 – Zifre 2009-04-26 19:29:49

7

如果您瞭解它,可以打破保留週期。通常它會導致令人討厭的錯誤(內存泄漏)。在你的例子中:

A* a = [[A alloc] initAndCreateB]; 

現在,一個未命名的B實例(由A創建)的保留計數爲1。由於我們堅持A中的參考和匿名乙實例持有的強引用A,A的保留計數爲2

比方說,我們使用的是做:

[a release]; 
return 12; 

現在,A的保留計數是1.它不會被釋放,它的內存會丟失。這就是保留週期不好的原因。

3

打破保留循環的方法是有一個單獨的「關閉」方法。

A retains B 
B retains A 

當你做,調用一個方法在A,其中A釋放B.(我稱之爲「close」),則可以釋放A和整個循環將發佈(假設其他地方沒有保留)。

相關問題