Я знаю, что блок Objective-C может захватывать и устанавливать значение переменных вне его охватывающей области. Как это делается?
Как Блок захватывает переменные за пределами своей охватывающей области?
Ответ 1
На самом деле это довольно просто и описано в спецификации реализации блоков Clang в разделе "Импортированные переменные" .
Когда компилятор встречает блок вроде:
^{ if( numBalloons > numClowns) abort(); }
он создает литеральную структуру, которая включает, помимо прочего, два элемента, которые здесь важны. Там указатель функции для исполняемого кода в блоке и поле const
для каждой переменной, которая упоминается внутри блока. Что-то вроде этого:
struct __block_literal_1 {
/* other fields */
void (*invoke)(struct __block_literal_1 *);
/* ... */
const int numBalloons;
const int numClowns;
};
Обратите внимание, что функция invoke
примет указатель на структуру, которая определена здесь; то есть блок выполняет сам код при выполнении своего кода. Таким образом, код получает доступ к членам структуры.
Сразу после объявления компилятор создает определение блока, которое просто использует ссылочные переменные для инициализации правильных полей в struct
:
struct __block_literal_1 __block_literal_1 = {
/* Other fields */
__block_invoke_2, /* This function was also created by the compiler. */
/* ... */
numBalloons, /* These two are the exact same variables as */
numClowns /* those referred to in the Block literal that you wrote. *
};
Затем внутри функции invoke
ссылки на захваченные переменные выполняются подобно любому другому члену структуры, the_block->numBalloons
.
Ситуация для переменных типа объекта несколько сложнее, но применяется тот же принцип.
Ответ 2
Внутри блока объектов блока кода переменные могут обрабатываться пятью различными способами.
Вы можете ссылаться на три стандартных типа переменных, как и на функцию:
- Глобальные переменные, включая статические локали
- Глобальные функции (которые arent являются технически переменными)
- Локальные переменные и параметры из закрывающей области
Блоки также поддерживают два других типа переменных:
На уровне функции находятся переменные
__block
. Они изменяются внутри блока (и охватывающей области) и сохраняются, если какой-либо ссылочный блок копируется в кучу.
const
импорт.Наконец, в реализации метода блоки могут ссылаться на переменные экземпляра Objective-C - см. Object и Block Variables.
Для переменных, используемых в блоке, применяются следующие правила:
Доступны глобальные переменные, включая статические переменные, которые существуют в охватывающей лексической области.
Доступны параметры, переданные блоку (как и параметры для функции).
Стек (нестатические) переменные, локальные для охватывающей лексической области, записываются как переменные
const
.Их значения берутся в точке блочного выражения внутри программы. Во вложенных блоках значение захватывается из ближайшей охватывающей области.
Переменные, локальные для охватывающей лексической области, объявленные с помощью модификатора хранилища
__block
, предоставляются посредством ссылки и поэтому изменяемы.Любые изменения отражаются в охватывающей лексической области, включая любые другие блоки, определенные в пределах той же охватывающей лексической области. Они более подробно обсуждаются в разделе Тип хранения __block.
Локальные переменные, объявленные в лексической области блока, которые ведут себя как локальные переменные в функции.
Каждый вызов блока предоставляет новую копию этой переменной. Эти переменные, в свою очередь, могут использоваться как
const
или переменные-ссылки в блоках, заключенных в блок.
Ответ 3
В принципе, "объект" блока содержит переменную внутри объекта блока (например, "переменную экземпляра" блочного объекта) для каждой захваченной локальной переменной. (Ответ Josh Caswell дает более подробную информацию о том, как он реализован.) Когда блок создается, значение каждой захваченной локальной переменной в это время копируется в соответствующую переменную внутри блока. Всякий раз, когда переменная используется внутри блока, она использует эту переменную внутри блока.