В Objective-C я знаю, что блоки считаются объектами, поэтому мне было интересно, можно ли хранить их в массиве. Возникает вопрос, являются ли блоки объектами первого класса или они просто рассматриваются как объекты для передачи их между объектами? Если они являются объектами первого класса, то разве они не должны храниться в массивах?
Хранение блоков в массиве
Ответы (2)
EDIT: не вдаваясь в подробности, в ARC теперь вы можете добавлять блоки в коллекции, как и любой другой объект (см. обсуждение).
Ниже я оставил исходный ответ нетронутым, так как он содержит некоторые интересные технические детали.
Возникает вопрос, являются ли блоки объектами первого класса или они просто рассматриваются как объекты для передачи их между объектами? Если они являются объектами первого класса, то разве они не должны храниться в массивах?
Блоки — это объекты Objective-C, которые во многом ведут себя так же, как и любой другой объект NSObject, с несколькими ключевыми отличиями:
Блоки всегда генерируются компилятором. Они эффективно «распределяются/инициируются» во время выполнения, когда выполнение проходит через объявление блоков.
Блоки изначально создаются в стеке. Block_copy() или метод
copy
должен использоваться для перемещения блока в кучу, если блок должен пережить текущую область (см. пункт ARC ниже).Блоки на самом деле не имеют вызываемого API, кроме управления памятью.
Чтобы поместить блок в коллекцию, его сначала нужно скопировать.
Всегда. В том числе в рамках ARC.(См. комментарии.) Если вы этого не сделаете, существует риск того, что блок, выделенный в стеке, будетautoreleased
, и ваше приложение впоследствии выйдет из строя.Копирование блока на основе стека также скопирует все захваченное состояние. Если вы делаете несколько копий блока, более эффективно скопировать его один раз, а затем копировать копию (поскольку копирование копии просто увеличивает количество сохранений, поскольку блоки неизменяемы).
В ARC возврат блока из метода или функции «просто работает»; он будет автоматически скопирован в кучу, а возврат будет фактически блоком с автоматическим освобождением (компилятор может оптимизировать автоматическое освобождение при определенных обстоятельствах). Даже с ARC вам все равно нужно скопировать блок, прежде чем вставлять его в коллекцию.
Я написал несколько сообщений в блоге, в которых содержится введение в блоки и некоторые советы и рекомендации. Вы можете найти их интересными.
И да, добавлять их в словари весьма полезно. Я написал пару фрагментов кода, в которых я помещал блоки в словари в качестве обработчиков команд, где ключом было имя команды. Очень кстати.
llvm
. Я подозреваю, но еще не проверил, что документация разработчиков iOS7 тоже будет обновлена (по крайней мере, я надеюсь, что они есть).
- person bbum; 14.09.2013
llvm
, поставляемой с Xcode 5 (Apple LLVM 5.0). Бага больше нет, поэтому блоки можно спокойно хранить в контейнерах без явного копирования под ARC.
- person Gabriele Petronella; 07.10.2013
addObject:
(например), копируется.
- person newacct; 13.11.2013
NSArray
содержит строгую ссылку на свои элементы, когда они добавляются в коллекцию. Это означает, что ARC вставит retain
, чтобы соответствовать семантике строгой ссылки на элементы. В случае блочных указателей он вставит Block_Copy
в соответствии со спецификацией clang.
- person Gabriele Petronella; 13.11.2013
NSArray
делает с элементом, который вы ему даете, находится в его внутренней реализации. Foundation компилируется Apple, и вы не компилируете этот код самостоятельно; поэтому, используете ли вы ARC или MRC в своей программе, не имеет значения. Даже если мы рассмотрим, как Foundation компилируется Apple, независимо от того, использовали ли они ARC или MRC для компиляции кода NSArray, он не будет выполнять копирование в соответствии со спецификацией ARC, потому что элемент будет иметь тип id
, т. е. универсальный указатель объекта. тип, и спецификация ARC не преобразует сохранение в копию для значения этого типа.
- person newacct; 13.11.2013
copy
элементу во время выполнения, потому что код NSArray работает с id
и делает то же самое (отправляет одни и те же сообщения) для всех объектов. Сообщение, которое он отправляет добавленным объектам, — retain
. Это сообщение, которое вызывается во время выполнения. Период. Сообщение retain
, вызванное в блоке стека, не возвращает его копию. ARC не имеет значения, потому что ARC выполняется во время компиляции.
- person newacct; 18.11.2013
Да, блоки действительно являются объектами, и вы можете поместить их в массивы:
NSMutableArray *arr = [NSMutableArray new];
[arr addObject:^(){NSLog(@"my block");}];
void (^ myblock)() = [arr objectAtIndex:0];
myblock();
это поместит «мой блок» в консоль.
NSArrays
делают retain
свои объекты внутри (они всегда так делали и делают до сих пор), и если вы сейчас прочитаете последний абзац главы 7.5, там говорится, что каждое сохранение типа блочного указателя должно иметь тот же эффект, что и Block_copy()
. Поэтому, когда NSArray
сохраняет объект, который на самом деле является указателем блока, это сохранение должно вести себя как Block_copy()
. Таким образом, нет необходимости явно копировать блоки перед их передачей в коллекцию, подобную NSArray
, и если это требовалось в прошлом, это была ошибка в компиляторе.
- person Mecki; 09.11.2013
NSArray
сохраняет элементы, добавленные к нему во внутреннем коде, которые вы не видите. В этом внутреннем коде, даже если предположить, что он написан с использованием ARC, переменная не будет иметь тип блочного указателя, потому что все методы NSArray
принимают тип id
. На самом деле, ARC/MRC — это вещь времени компиляции, и она не имеет отношения к уже скомпилированному коду, такому как классы Foundation.
- person newacct; 09.11.2013
retain
блока стека, созданного в рамках ARC, указывает на Block_copy
внутри, поэтому вызов функции сохранения для такого блока (из кода ARC или из кода, отличного от ARC) будет всегда копировать блок (после этого указатель изменится на Block_retain
). ). Вы можете легко убедиться в этом с помощью отладчика. См. здесь: pastebin.com/rcCncCy4 Вызов retain
для блока приводит к копированию блока, но только потому, что сам блок был создан в коде ARC.
- person Mecki; 11.11.2013
someBlock
, переменной __strong
типа указателя блока, вы вызываете копию там, в коде ARC.
- person newacct; 12.11.2013
Block_copy
; это может даже нарушить оптимизацию компилятора.
- person Mecki; 12.11.2013
Block_copy
вместо сохранения, если он является блоком. Тот факт, что метод объявлен как принимающий id
, этого не меняет. Если вы этого не понимаете, я не уверен, что смогу вам помочь.
- person jbg; 09.07.2014
[NSMutableArray addObject:]
или аналогичному. Однако позже сам NSArray будет хранить этот блок со строгой семантикой (как и со всеми хранящимися в нем объектами), а спецификация ARC требует, чтобы это было эквивалентно Block_copy
. Раньше LLVM не следовал этой части спецификации, которая была ошибкой, а теперь следует.
- person jbg; 09.07.2014
Block_copy
. NSArray не скомпилирован вами. Вы не знаете, компилируется ли он в ARC, и в любом случае это не имеет значения, потому что в любом случае это будет то же самое - внутри реализации метода NSArray нет переменной типа времени компиляции указателя блока.
- person newacct; 09.07.2014
retain
, чтобы иметь семантику копирования. Это соответствует спецификации ARC (в которой говорится об изменении сохранения для копирования семантики во время компиляции) и может быть легко проверено на любой версии компилятора. Трудно представить, как это могло бы работать в любом случае, потому что проект может смешивать код ARC и не-ARC, так как же внутренний метод NSArray (скомпилированный Apple) должен знать, используется ли он из ARC или нет?
- person newacct; 09.07.2014
@
. Я ничего не спорю, и мне не нужна помощь. Я тот, кто исправляет ошибку в ответе. Я говорю то, что ясно говорит спецификация. Никакая проверка не может показать, что что-то правильно, потому что это может быть неопределенное поведение. Вы не указали и не обосновали ничего, с чем вы не согласны в том, что я сказал.
- person newacct; 11.08.2014