Почему делегатам Objective-C обычно присваивается присвоение свойства вместо сохранения?

Я занимаюсь серфингом в прекрасном блоге, поддерживаемом Скоттом Стивенсоном, и я пытаюсь понять фундаментальную концепцию Objective-C присвоения делегатам свойства "присваивать" vs "сохранить". Обратите внимание, что оба они одинаковы в среде сбора мусора. Меня больше всего интересует среда, не основанная на GC (например, iPhone).

Непосредственно из блога Скотта:

"Ключевое слово assign генерирует установщик, который напрямую присваивает значение переменной экземпляра, а не копирует или сохраняет его. Это лучше всего подходит для таких примитивных типов, как NSInteger и CGFloat, или для объектов, которые вы непосредственно не используете, например делегаты".

Что означает, что вы непосредственно не владеете объектом делегата? Обычно я сохраняю своих делегатов, потому что, если я не хочу, чтобы они уходили в пропасть, удержание позаботится об этом для меня. Обычно я отклоняю UITableViewController от своего соответствующего источника данных и делегата. Я также сохраняю этот конкретный объект. Я хочу убедиться, что это никогда не исчезнет, ​​поэтому мой UITableView всегда имеет свой делегат.

Может кто-то еще объяснить, где/почему я ошибаюсь, поэтому я могу понять эту общую парадигму в Objective-C 2.0 программировании использования свойства assign на делегатах вместо сохранения?

Спасибо!

Ответ 1

Причина, по которой вы избегаете сохранения делегатов, заключается в том, что вам нужно избегать цикла сохранения:

A создает B A устанавливает себя как делегат B ... A выпущен его владельцем

Если B сохранил A, A не будет выпущен, так как B владеет A, таким образом A dealloc никогда не будет вызван, что приведет к утечке A и B.

Вы не должны беспокоиться о том, что он уходит, потому что он владеет B и таким образом избавляется от него в dealloc.

Ответ 2

Поскольку объект, отправляющий сообщения делегата, не владеет делегатом.

Во многих случаях это происходит наоборот, когда контроллер устанавливает себя как делегат вида или окна: контроллер владеет окном/окном, поэтому, если в представлении/окне принадлежит его делегат, оба объекта будут владеть друг друга. Это, конечно, цикл сохранения, аналогичный утечке с тем же последствием (объекты, которые должны быть мертвы, остаются в живых).

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

В любом случае объект с делегатом не должен сохранять свой делегат.

(Между прочим, хотя бы одно исключение. Я не помню, что это было, и я не думаю, что для этого была веская причина.)


Добавление (добавлено 2012-05-19): В ARC вы должны использовать weak вместо assign. Слабые ссылки автоматически устанавливаются в nil, когда объект умирает, исключая возможность того, что делегирующий объект в конечном итоге отправит сообщения мертвому делегату.

Если вы почему-то избегаете ARC, по крайней мере измените свойства assign, которые указывают на объекты на unsafe_unretained, которые делают явным, что это недостижимая, но не обнуляющая ссылка на объект.

assign остается подходящим для значений без объекта как в ARC, так и в MRC.

Ответ 3

Обратите внимание, что когда у вас есть назначенный делегат, очень важно всегда устанавливать значение делегирования в нуле всякий раз, когда объект будет освобожден - поэтому объект всегда должен быть осторожным, чтобы исключить ссылки делегата в dealloc, если он не сделал этого в другом месте.

Ответ 4

Одна из причин этого - избегать циклов сохранения. Просто чтобы избежать сценария, где A и B оба объекта ссылаются друг на друга, и ни один из них не освобождается из памяти.

Acutally присваивать лучше всего для примитивных типов, таких как NSInteger и CGFloat, или объектов, которые вы не непосредственно владеете, например делегатов.