Помните также, что циклы сохранения могут происходить, если ваш блок ссылается на другой объект, который затем сохраняет self
.
Я не уверен, что Сборка мусора может помочь в этих циклах сохранения. Если объект, сохраняющий блок (который я назову объектом сервера), переживет self
(клиентский объект), ссылка на self
внутри блока не будет считаться циклической, пока сам удерживающий объект не будет освобожден. Если объект сервера далеко переживает своих клиентов, у вас может быть значительная утечка памяти.
Поскольку чистых решений не существует, я бы порекомендовал следующие обходные пути. Не стесняйтесь выбирать один или несколько из них, чтобы решить вашу проблему.
- Использовать блоки только для завершения , а не для открытых событий. Например, используйте блоки для таких методов, как
doSomethingAndWhenDoneExecuteThisBlock:
, а не такие методы, как setNotificationHandlerBlock:
. Блоки, используемые для завершения, имеют определенный срок службы и должны быть освобождены объектами сервера после их оценки. Это предотвращает слишком длительный цикл сохранения, даже если он имеет место.
- Сделайте тот танец слабой ссылки, который вы описали.
- Предоставляет метод для очистки вашего объекта перед его освобождением, который «отключает» объект от объектов сервера, которые могут содержать ссылки на него; и вызовите этот метод перед вызовом release для объекта. Хотя этот метод вполне подходит, если у вашего объекта есть только один клиент (или он является единичным в некотором контексте), но он сломается, если у него несколько клиентов. Вы в основном побеждаете здесь механизм сохранения счета; это похоже на
dealloc
вместо release
.
Если вы пишете объект сервера, принимайте аргументы блока только для завершения. Не принимайте аргументы блока для обратных вызовов, таких как setEventHandlerBlock:
. Вместо этого вернитесь к классическому шаблону делегата: создайте формальный протокол и объявите метод setEventDelegate:
. Не оставляйте делегата. Если вы даже не хотите создавать формальный протокол, примите селектор в качестве обратного вызова делегата.
И, наконец, этот шаблон должен вызывать сигналы тревоги:
- (void)dealloc {
[myServerObject releaseCallbackBlocksForObject:self];
...
}
Если вы пытаетесь отцепить блоки, которые могут ссылаться на self
изнутри dealloc
, у вас уже есть проблемы. dealloc
никогда не может быть вызван из-за цикла сохранения, вызванного ссылками в блоке, что означает, что ваш объект будет просто утекать, пока объект сервера не будет освобожден.