Определение блоков Objective-C как свойств - лучшая практика
Недавно я натолкнулся на документ Apple, в котором показано следующее объявление свойства для блока:
@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end
Также в этой статье говорится:
Примечание. Вы должны указать copy в качестве атрибута свойства, поскольку необходимо скопировать блок, чтобы отслеживать его захваченное состояние за пределами исходной области. Это не то, о чем вам нужно беспокоиться при использовании автоматического подсчета ссылок, так как это произойдет автоматически, но лучше всего, чтобы атрибут свойства отображал результирующее поведение. Для получения дополнительной информации см. Темы программирования блоков.
Я также прочитал предложенные темы программирования блоков, но там не нашел ничего подходящего.
Мне все еще интересно, почему определение свойства блока как "копии" является лучшей практикой. Если у вас есть хороший ответ, попробуйте различить различия между ARC и MRC, если таковые имеются.
Спасибо
3 ответа
По умолчанию блоки создаются в стеке. Это означает, что они существуют только в том объеме, в котором они были созданы.
Если вы хотите получить к ним доступ позже, их нужно скопировать в кучу, отправив copy
сообщение для объекта блока. ARC сделает это за вас, как только обнаружит, что к блоку нужно обратиться за пределами области, в которой он создан. Рекомендуется объявлять любое свойство блока как копию, потому что так оно и должно быть при автоматическом управлении памятью.
Прочитайте Mike Ash об объектах Stack и Heap в Objective-C, чтобы получить больше информации о стеке и куче.
Блоки по умолчанию размещаются в стеке. Это оптимизация, поскольку выделение стека намного дешевле, чем выделение кучи. Распределение стека означает, что по умолчанию блок снова прекратит свое существование после выхода из области, в которой он объявлен. Таким образом, свойство блока с retain
семантика приведет к висячему указателю на блок, который больше не существует.
Чтобы переместить блок из стека в кучу (и тем самым придать ему нормальную семантику управления памятью Objective-C и продлить время жизни), вы должны скопировать блок с помощью [theBlock copy]
, Block_copy(theBlock)
и т. д. Оказавшись в куче, можно управлять временем жизни блока, сохраняя / освобождая его. (Да, это относится и к ARC, вам просто не нужно звонить -retain
/-release
сам.)
Итак, вы хотите объявить свойства блока с copy
семантика, поэтому блок копируется, когда свойство установлено, избегая висящего указателя на основанный на стеке блок.
"Лучшие практики", на которые вы ссылаетесь, просто говорят: "Поскольку ARC собирается магически копировать ваш блок, независимо от того, что вы здесь пишете, лучше всего явно писать" копировать ", чтобы не путать будущие поколения, глядя на ваш код".
Объяснение следует:
Как правило, вам не нужно копировать (или сохранять) блок. Вам нужно сделать копию, только когда вы ожидаете, что блок будет использоваться после уничтожения области, в которой он был объявлен. Копирование перемещает блок в кучу.
–Блоки программирования Темы: Использование блоков, Копирование блоков
Очевидно, что присвоение блока свойству означает, что его можно использовать после уничтожения области, в которой оно было объявлено. Таким образом, согласно темам программирования блоков, этот блок должен быть скопирован в кучу с Block_copy
,
Но ARC позаботится об этом за вас:
Блоки "просто работают", когда вы передаете блоки вверх по стеку в режиме ARC, например, при возврате. Вам больше не нужно звонить в Block Copy.
- Переход на ARC
Обратите внимание, что это не о retain
семантика блока. Для контекста блока просто нет возможности существовать без перемещения из стека (который скоро появится) и в кучу. Таким образом, независимо от того, какие атрибуты вы квалифицируете @property
с, ARC все еще собирается скопировать блок.