2009-04-26 3 views
10

Есть два объекта A и B. A создает B и сохраняет его. B имеет переменную экземпляра, которая указывает на A, сохраняя ее. Так что оба сохраняют друг друга. Некоторые люди говорят, что эта сильная связь больше не может быть нарушена.Сохраняемые циклы: Почему это так плохо?

Но это действительно так?

Если B освободит A, тогда A может легко освободить B, и поэтому B будет освобожден. A будет освобожден, как только он станет другим владельцем (я думаю, должен быть кто-то) освобождает его.

Или эта проблема применима только в случае, когда A не создает B, а просто содержит сильную ссылку на нее, сохраняя ее в переменной экземпляра? Я до сих пор не понимаю, почему эта связь не может быть снова разорвана.

ответ

15

Циклы не плохие, но их часто избегают, потому что они могут сделать это сложным, чтобы убедиться, что у вас нет утечек памяти. Утечки возникают, особенно когда объекты «подсчет ссылок». На языке или системе, использующей подсчет ссылок, объект отслеживает количество ссылок, указывающих на него. Каждый раз, когда ссылка удаляется, счетчик уменьшается, когда счетчик достигает нуля, ссылок нет и поэтому объект может быть удален.

Это обычно заботится о себе и работает нормально, без тщательного размышления. Если у вас есть группа объектов без циклов, и вы отбрасываете ссылку на корневой объект, тогда она будет удалена, это означает, что ссылки, которые она имеет на принадлежащие ей объекты, будут удалены, объекты, на которые делается ссылка, будут иметь свои подсчеты ссылок перейти к нулю. Они будут удалены, и каскад приведет к удалению всех объектов.

Но ... если у вас есть цикл, этот каскад не работает. У вас может быть группа объектов, и вы не хотите их больше, поэтому вы бросаете единственную ссылку, которую вы имеете на эти объекты, но поскольку есть цикл, объекты ссылаются друг на друга. Это означает, что их счетчики ссылок никогда не обращаются к нулю, и они не удаляются. Это утечка памяти.

Очевидно, что вы можете провести тщательное управление и разбить циклы, прежде чем отказаться от ссылки на группу объектов, которые вы больше не хотите. Но ... как я только что сказал, это требует тщательного управления. Очень легко ошибиться. Это одна из основных причин утечки памяти.

Во избежание риска утечек и сложной работы по разрыву циклов, когда вам больше не нужна группа объектов, программисты обычно стараются избегать циклов. Это становится более важным в крупных проектах со многими программистами, где никто не понимает всю систему. Если бы были циклы, программистам пришлось бы следить и долго проводить изучение кода друг друга, чтобы избежать циклов.

Некоторые языки с сборщиками мусора (например, C#) могут удалять группу объектов, которые больше не нужны, даже если группа содержит циклы.

+2

Сборщик мусора Objective-C (если включен) также может удалять группы удерживания (почти любой сборщик мусора), но это не относится к iPhone, где сбор мусора Objective-C не поддерживается. –

2

Проблема заключается в следующем: A указывает на B и сохраняет B, а B указывает и сохраняет A. Когда нет других ссылок на A или B, их освободить не будет, поскольку приложение не поддерживает есть ссылки на них в этот момент. Это называется эталонным циклом, и он представляет собой тип утечки памяти, общий в любой системе подсчета ссылок. То, как это решается на большинстве языков высокого уровня, - это использование сборки мусора, а не подсчета ссылок.

+0

Спасибо. Но почему не должно быть никакой ссылки на A или B? В этом случае у меня есть два объекта, в которых мое приложение не имеет к ним ссылки? – Thanks

+0

Если у вас есть другие ссылки на A или B, нет проблем. Только когда вы теряете эти ссылки (например, если они выходят из области), это проблема. – Zifre

7

Сохранение цикла может быть нарушено, если вы знаете об этом. Обычно это приводит к неприятным ошибкам (утечка памяти). В вашем примере:

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

Теперь экземпляр без имени B (созданный A) имеет показатель сохранения 1.Поскольку мы считаем ссылку на А и анонимный экземпляр B имеет сильную ссылку на А, сохранить подсчитывать равен 2.

Допустим, мы сделали с помощью A:

[a release]; 
return 12; 

Теперь, сохраняют подсчет 1. Он не будет выпущен, это потеря памяти. Вот почему сохранить циклы плохие.

+0

Спасибо. Для моего понимания это не похоже, если есть два разных объекта. A должен быть суперклассом B, так что структура данных B может вписываться в A. Но тогда после этого не было бы просто анонимным B? Я могу быть не прав. Уже поздно;) – Thanks

+0

A не обязательно должен быть суперклассом B. Он просто имеет ivar, который указывает на экземпляр B. B - отдельный объект. Подумайте о классах, которые вы видите каждый день: у вас может быть NSArray, а NSString и NSDate - как переменные экземпляра. Это * не * подклассы вашего пользовательского класса. Это независимые классы, которые вы используете. – Chuck

3

Способ разрыва цикла удержания состоит в том, чтобы иметь отдельный метод «закрыть».

т.е.

A retains B 
B retains A 

, когда вы закончите, вызовите метод (я буду называть его «close») на А, где А высвобождает B. Вы можете выпустить и весь цикл будет выпускать (предполагая, что в другом месте не сохранилось).

Смежные вопросы