it-swarm.com.ru

Какао блоки как сильные указатели против копирования

Я работал несколько раз с блоками, как с указателями, на которые я имел сильную ссылку

Я слышал, что вы должны использовать копирование, но что подразумевается при работе с блоками как указателями, а не с необработанным объектом?

Я никогда не получал жалобу от компилятора, что я не должен использовать

@property (nonatomic, strong) MyBlock block;

но следует использовать

@property (nonatomic, copy) MyBlock block;

насколько я знаю, блок - это просто объект, так почему же в любом случае предпочтительнее копировать?

18
Peter Lapisu

Короткий ответ

Ответ это исторический, вы совершенно правы, что в текущем коде ARC нет необходимости использовать copy и свойство strong вполне подойдет. То же самое касается, например, локальных и глобальных переменных.

Длинный ответ

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

Сравните это с локальными переменными , которые (a), созданные в стеке , (b), автоматически уничтожаются, когда владеющая функция/метод возвращает и (c) может быть передан по адресу в методы/функции, вызываемые функцией-владельцем. Адрес локальной переменной не может быть сохранен и использован после того, как возвращена его собственная функция/метод - переменная больше не существует.

Однако ожидается, что объекты превзойдут свою функцию/метод создания (если требуется), поэтому в отличие от локальных переменных они размещаются в куче и не уничтожаются автоматически в зависимости от возврата созданной ими функции/метода, а скорее в зависимости от того, нужны ли они по-прежнему, а ARC в эти дни автоматически определяет "потребность".

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

Когда реализация блока была впервые выпущена, оптимизация хранения блоков в стеке стала видна программистам, поскольку компилятор в то время не мог автоматически обрабатывать перемещение блока в кучу, когда это необходимо - программистам для этого приходилось использовать функцию block_copy() самих себя.

Хотя этот подход может быть неуместным в мире C низкого уровня (а блоки - конструкцией C), наличие высокоуровневых программистов Objective-C вручную управляющих оптимизацией компилятора на самом деле не очень хорошо. Как Apple выпустила новые версии улучшений компилятора, где были сделаны. Ранее программистам было сказано, что они могут заменить block_copy(block) на [block copy], вписываясь в обычные объекты Objective-C. Затем компилятор начал автоматически копировать блоки из стека по мере необходимости, но это не всегда было официально задокументировано.

Некоторое время не было необходимости вручную копировать блоки из стека, хотя Apple не может сбрасывать со счетов свое происхождение и называет это "наилучшей практикой", что, безусловно, является дискуссионным. В последней версии, сентябрь 2014 г., Apple Работа с блоками , они заявили, что в свойствах с блочными значениями следует использовать copy, но затем сразу же появятся чистые ( выделение добавил):

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

Нет необходимости "показывать результирующее поведение" - сохранение блока в стеке в первую очередь является оптимизацией и должно быть прозрачным для кода - как и другие оптимизации компилятора код должен получить выигрыш в производительности без участия программиста.

Поэтому, пока вы используете ARC и текущие компиляторы Clang, вы можете обрабатывать блоки как другие объекты, и поскольку блоки являются неизменяемыми, это означает, что вам не нужно их копировать. Доверяйте Apple, даже если они кажутся ностальгирующими по "старым добрым временам, когда мы все делали вручную", и призываем вас оставлять исторические напоминания в вашем коде, copy не требуется.

Ваша интуиция была права.

НТН

45
CRD

Вы спрашиваете о модификаторе владения для собственности. Это влияет на синтезированный (или автоматически синтезированный) геттер и/или сеттер для свойства, если оно синтезировано (или автоматически синтезировано).

Ответ на этот вопрос будет отличаться между MRC и ARC.

  • В MRC модификаторы владения недвижимостью включают assign, retain и copy. strong был введен в ARC, и когда strong используется в MRC, он является синонимом retain. Таким образом, вопрос будет касаться разницы между retain и copy, и есть большая разница, потому что установщик copy сохраняет копию заданного значения.

    Блоки должны быть скопированы для использования вне области, в которой они были созданы (с литералом блока). Поскольку ваше свойство будет хранить значение как переменную экземпляра, которая сохраняется при вызовах функций, и возможно, что кто-то назначит незанятый блок из области, в которой он был создан, соглашение заключается в том, что вы должны его скопировать. copy является модификатором права собственности.

  • В ARC strong делает базовую переменную экземпляра __strong, а copy также делает ее __strong и добавляет семантику копирования в установщик. Однако ARC также гарантирует, что при сохранении значения в переменную __strong типа указателя блока выполняется копирование. Ваше свойство имеет тип MyBlock, который, как я полагаю, является typedef для типа указателя блока. Следовательно, копия все равно будет сделана в установщике, если квалификатор владения был strong. Таким образом, в ARC нет разницы между использованием strong и copy для этого свойства.

Если это объявление может использоваться как в MRC, так и в ARC (например, заголовок в библиотеке), было бы неплохо использовать copy, чтобы оно работало правильно в обоих случаях.

3
newacct
what is the implication in working with blocks as pointers and not with the raw object?

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

Глядя на ваш конкретный пример, я предполагаю, что вы хотите сохранить блок, "так почему же в любом случае предпочтительнее копировать" enter code here? Ну, это вопрос безопасности (этот пример взят из блога Майка Эша). Поскольку блоки расположены в стеке (а не в куче, как остальные объекты в target-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]();

Цитируя Майка Эша о различных результатах:

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

2
Rui Peres

Примечание: Вы должны указать copy в качестве атрибута свойства, потому что необходимо скопировать блок, чтобы отслеживать его захваченное состояние за пределами исходной области видимости. Это не то, о чем вам нужно беспокоиться при использовании автоматического подсчета ссылок, так как это произойдет автоматически, но лучше всего, чтобы атрибут свойства отображал результирующее поведение. Для получения дополнительной информации см. Блокирует темы программирования .

0
Tai Le