Как Блок захватывает переменные за пределами своей охватывающей области?

Я знаю, что блок 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 или переменные-ссылки в блоках, заключенных в блок.

Отсюда:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html

Ответ 3

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