Cocoa блокирует сильные указатели против копии
Я работал несколько раз с блоками, как с указателями, к которым у меня была сильная ссылка
Я слышал, что вы должны использовать копию, но , что подразумевается при работе с блоками как указатели, а не с необработанным объектом?
Я никогда не получал жалобы от компилятора, что я не должен использовать
@property (nonatomic, strong) MyBlock block;
но следует использовать
@property (nonatomic, copy) MyBlock block;
насколько я знаю, блок - это всего лишь объект, поэтому зачем вообще копировать?
Ответы
Ответ 1
Короткий ответ
Ответ в том, что он исторический, вы совершенно правы, что в текущем коде ARC нет необходимости использовать copy
и strong
свойство вполне подойдет. То же самое касается, например, локальных и глобальных переменных.
Длинный ответ
В отличие от других объектов, блок может храниться в стеке, это оптимизация реализации и, как таковая, должна, как и другие оптимизации компилятора, не оказывать прямого влияния на написанный код. Эта оптимизация выгодна в общем случае, когда блок создается, передается как аргумент метода/функции, используется этой функцией, а затем отбрасывается - блок может быть быстро размещен в стеке и затем удален без кучи (динамический пул памяти) быть вовлеченным
Сравните это с локальными переменными, которые (a) созданы в стеке, (b) автоматически уничтожаются при возврате функции/метода-владельца и (c) могут передаваться по адресу в методы/функции, вызываемые функцией-владельцем. Адрес локальной переменной не может быть сохранен и использован после возврата ее владельцем функции/метода - переменная больше не существует.
Однако ожидается, что объекты превзойдут свою функцию/метод создания (если требуется), поэтому, в отличие от локальных переменных, они размещаются в куче и не уничтожаются автоматически при возврате функции/метода создания, а в зависимости от того, нужны ли они по-прежнему - и "потребность" здесь определяется АРК в эти дни автоматически.
Создание блока в стеке может оптимизировать общий случай, но это также вызывает проблему - если блок должен пережить своего создателя, как это часто делают объекты, то он должен быть перемещен в кучу, прежде чем его стек создателей будет уничтожен.
Когда реализация блока была впервые выпущена, оптимизация хранения блоков в стеке стала видна программистам, поскольку компилятор в то время не мог автоматически обрабатывать перемещение блока в кучу, когда это необходимо - программистам приходилось использовать функцию block_copy()
для сделай это сам.
Хотя этот подход может быть неуместным в мире C низкого уровня (а блоки - конструкцией C), наличие высокоуровневых программистов Objective-C вручную управляющих оптимизацией компилятора на самом деле не очень хорошо. Как Apple выпустила новые версии улучшений компилятора, где сделаны. Ранее программистам было сказано, что они могут заменить block_copy(block)
на [block copy]
, вписываясь в обычные объекты Objective-C. Затем компилятор начал автоматически копировать блоки из стека по мере необходимости, но это не всегда было официально задокументировано.
Некоторое время не было необходимости вручную копировать блоки из стека, хотя Apple не может отмахнуться от своего происхождения и называет это "наилучшей практикой", что, безусловно, является дискуссионным. В последней версии, сентябрь 2014 г., Apple Работая с блоками, они заявили, что для свойств с блочными значениями следует использовать copy
, но затем сразу же приводить в порядок (выделение добавлено):
Примечание: Вы должны указать copy в качестве атрибута свойства, потому что необходимо скопировать блок, чтобы отслеживать его захваченное состояние за пределами исходной области видимости. Это не то, о чем вам нужно беспокоиться при использовании автоматического подсчета ссылок, поскольку это произойдет автоматически, но для атрибута свойства лучше всего показывать результирующее поведение.
Нет необходимости "показывать результирующее поведение" - хранение блока в стеке в первую очередь является оптимизацией и должно быть прозрачным для кода - так же, как и при других оптимизациях компилятора, код должен получить выигрыш в производительности без участия программиста.
Поэтому, пока вы используете ARC и текущие компиляторы Clang, вы можете обрабатывать блоки как другие объекты, и поскольку блоки являются неизменяемыми, это означает, что вам не нужно их копировать. Доверяйте Apple, даже если они кажутся ностальгирующими по "старым добрым временам, когда мы делали вещи вручную", и призывают вас оставлять исторические напоминания в вашем коде, copy
не требуется.
Ваша интуиция была права.
Ответ 2
Вы спрашиваете о модификаторе собственности для свойства. Это влияет на синтезированный (или автоматически синтезированный) геттер и/или сеттер для свойства, если он синтезирован (или автосинтезирован).
Ответ на этот вопрос будет отличаться между MRC и ARC.
-
В MRC модификаторы собственности включают assign
, retain
и copy
. strong
был введен с ARC, а когда strong
используется в MRC, он синонимом retain
. Таким образом, вопрос будет касаться разницы между retain
и copy
, и есть большая разница, потому что copy
setter сохраняет копию данного значения.
Блоки должны быть скопированы для использования вне области, где она была создана (с литералом блока). Поскольку ваше свойство будет хранить значение в виде переменной экземпляра, которая сохраняется во всех вызовах функций, и возможно, что кто-то назначит незанятый блок из области, где он был создан, соглашение заключается в том, что вы должны его скопировать. copy
является правомочным модификатором.
-
В ARC strong
создает базовую переменную экземпляра __strong
, а copy
также делает ее __strong
и добавляет семантику копирования в сеттер. Тем не менее, ARC также гарантирует, что всякий раз, когда значение сохраняется в переменной __strong
типа block-pointer, выполняется копия. Свойство имеет тип MyBlock
, который я предполагаю typedef для типа указателя блока. Таким образом, копия все равно будет выполнена в сеттере, если квалификатор собственности был strong
. Таким образом, в ARC нет разницы между использованием strong
и copy
для этого свойства.
Если это объявление может использоваться как в MRC, так и в ARC, хотя (например, заголовок в библиотеке), было бы неплохо использовать copy
, чтобы он работал корректно в обоих случаях.
Ответ 3
what is the implication in working with blocks as pointers and not with the raw object?
Вы никогда не используете исходное значение, у вас всегда есть указатель на блок: блок - это объект.
Глядя на ваш конкретный пример, я предполагаю, что вы хотите сохранить блок вокруг, ", так почему же предпочтительнее копировать? enter code here
? Ну, это вопрос безопасности (этот пример взят из блога Майка Эша). Поскольку блоки выделяются в стеке (а не в куче как остальные объекты в objective-c), когда вы делаете что-то вроде этого:
[dictionary setObject: ^{ printf("hey hey\n"); } forKey: key];
Вы выделяете блок в фрейме стека текущей области действия, поэтому, когда область действия заканчивается (например, возвращая словарь), кадр стека уничтожается, и блок идет с ним. Таким образом, у вас есть висячий указатель. Я бы посоветовал полностью прочитать статью Майка. Во всяком случае, вы можете использовать свойство strong
, если при назначении блока вы его скопируете:
self.block = [^{} copy];
Изменить:. Посмотрев дату публикации Майка, я предполагаю, что это было поведение Pre-ARC. На ARC кажется, что он работает так, как ожидалось, и он не сработает.
Edit2: После экспериментирования с не-ARC он также не разбивается. Но этот пример показывает разные результаты в зависимости от использования ARC или нет:
void (^block[10])();
int i = -1;
while(++i < 10)
block[i] = ^{ printf("%d\n", i); };
for(i = 0; i < 10; i++)
block[i]();
Цитата Майка Эша о различных результатах:
Причина, по которой он печатает десять девяток в первом случае, довольно проста: блок, созданный в цикле, имеет срок службы, привязанный к внутренняя область цикла. Блок уничтожается на следующей итерации цикл и при выходе из цикла. Конечно, "уничтожить" просто средства что его слот в стеке может быть перезаписан. Это просто случается, что компилятор каждый раз использует один и тот же слот через цикл, поэтому в конце массив заполняется одинаковыми указателями и таким образом, вы получаете одинаковое поведение.
Ответ 4
Примечание: Вы должны указать copy в качестве атрибута свойства, потому что необходимо скопировать блок, чтобы отслеживать его захваченное состояние за пределами исходной области видимости. Это не то, о чем вам нужно беспокоиться при использовании автоматического подсчета ссылок, поскольку это произойдет автоматически, но для атрибута свойства лучше всего показывать результирующее поведение. Для получения дополнительной информации см. Темы программирования блоков.
Ответ 5
Насколько я понимаю, copy
требуется, когда объект изменен. Используйте это, если вам нужно значение объекта, как оно есть в данный момент, и вы не хотите, чтобы это значение отражало любые изменения, сделанные другими владельцами объекта. Вам нужно будет освободить объект, когда вы закончите его, потому что вы сохраняете копию.
С другой стороны, strong
означает, что вы являетесь владельцем объекта, пока он не понадобится. Это замена атрибута retain
как части ARC.
Источник: Objective-C объявленные атрибуты @property (неатомные, скопированные, сильные, слабые)