Блоки и переменные

Эта статья описывает взаимодействие между блоками и переменными, включая управление памятью.

Типы переменной

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

Можно сослаться на три стандартных типа переменной, как Вы были бы от функции:

Блоки также поддерживают два других типа переменной:

  1. На функциональном уровне __block переменные. Если какой-либо блок ссылки копируется в «кучу», они являются непостоянными в блоке (и объем включения) и сохраняются.

  2. const импорт.

Наконец, в реализации метода, блоки могут сослаться на переменные экземпляра Objective C — посмотрите Переменные объекта и Основные переменные.

Соблюдающие правила применяются к переменным, используемым в блоке:

  1. Глобальные переменные доступны, включая статические переменные, существующие в лексическом контексте включения.

  2. Параметры, переданные блоку, доступны (точно так же, как параметры к функции).

  3. (Нестатические) переменные штабеля, локальные для лексического контекста включения, получены как const переменные.

    Их значения приняты при блочном выражении в программе. Во вложенных блоках значение получено от самого близкого объема включения.

  4. Переменные, локальные для лексического контекста включения, объявленного с __block модификатор хранения является предоставленным ссылкой и является непостоянным - также.

    Любые изменения отражаются в лексическом контексте включения, включая любые другие блоки, определенные в том же лексическом контексте включения. Они обсуждены более подробно в __ Тип блочной системы хранения.

  5. Локальные переменные объявили в лексическом контексте блока, которые ведут себя точно как локальные переменные в функции.

    Каждый вызов блока предоставляет новую копию той переменной. Эти переменные могут поочередно использоваться как const или переменные ссылкой в блоках включаются в блоке.

Следующий пример иллюстрирует использование локальных нестатических переменных:

int x = 123;
 
void (^printXAndY)(int) = ^(int y) {
 
    printf("%d %d\n", x, y);
};
 
printXAndY(456); // prints: 123 456

Как отмечено, пытаясь присвоить новое значение x в блоке привел бы к ошибке:

int x = 123;
 
void (^printXAndY)(int) = ^(int y) {
 
    x = x + y; // error
    printf("%d %d\n", x, y);
};

Чтобы позволить переменной быть измененной в блоке, Вы используете __block модификатор типа хранения — видит __ Тип блочной системы хранения.

__ тип блочной системы хранения

Можно указать, что импортированная переменная является непостоянной — т.е. чтение-запись — путем применения __block модификатор типа хранения. __block хранение является подобным, но взаимоисключающим из, register, auto, и static типы хранения для локальных переменных.

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

Как оптимизация, блочная система хранения начинается на штабеле — точно так же, как сами блоки делают. Если блок копируется с помощью Block_copy (или в Objective C, когда блок отправляется a copy), переменные копируются в «кучу». Таким образом, адрес a __block переменная может изменяться в течение долгого времени.

Существует два дальнейших ограничения на __block переменные: они не могут быть массивами переменной длины и не могут быть структурами, содержащими массивы переменной длины C99.

Следующий пример иллюстрирует использование a __block переменная:

__block int x = 123; //  x lives in block storage
 
void (^printXAndY)(int) = ^(int y) {
 
    x = x + y;
    printf("%d %d\n", x, y);
};
printXAndY(456); // prints: 579 456
// x is now 579

Следующий пример показывает взаимодействие блоков с несколькими типами переменных:

extern NSInteger CounterGlobal;
static NSInteger CounterStatic;
 
{
    NSInteger localCounter = 42;
    __block char localCharacter;
 
    void (^aBlock)(void) = ^(void) {
        ++CounterGlobal;
        ++CounterStatic;
        CounterGlobal = localCounter; // localCounter fixed at block creation
        localCharacter = 'a'; // sets localCharacter in enclosing scope
    };
 
    ++localCounter; // unseen by the block
    localCharacter = 'b';
 
    aBlock(); // execute the block
    // localCharacter now 'a'
}

Переменные объекта и основные переменные

Блоки предоставляют поддержку для Objective C и объектов C++ и других блоков, как переменные.

Объекты Objective C

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

  • При доступе к переменной экземпляра ссылкой сильная ссылка сделана, чтобы self;

  • При доступе к переменной экземпляра значением сильная ссылка сделана на переменную.

Следующие примеры иллюстрируют две различных ситуации:

dispatch_async(queue, ^{
    // instanceVariable is used by reference, a strong reference is made to self
    doSomethingWithObject(instanceVariable);
});
 
 
id localVariable = instanceVariable;
dispatch_async(queue, ^{
    /*
      localVariable is used by value, a strong reference is made to localVariable
      (and not to self).
    */
    doSomethingWithObject(localVariable);
});

Для переопределения этого поведения для определенной переменной объекта можно отметить его с __block модификатор типа хранения.

Объекты C++

В целом можно использовать объекты C++ в блоке. В функции членства ссылки на задействованные переменные и функции через неявно импортированы this указатель и таким образом кажется непостоянным. Существует два соображения, применяющиеся, если копируется блок:

  • Если у Вас есть a __block класс памяти для того, что было бы стековым объектом C++, тогда обычное copy конструктор используется.

  • При использовании какого-либо другого C++ стековый объект из блока это должно иметь a const copy конструктор. Объект C++ тогда копируется с помощью того конструктора.

Блоки

Если необходимый — все дерево может быть скопировано (с вершины), при копировании блока любые ссылки на другие блоки из того блока копируются. Если у Вас будут основные переменные, и Вы ссылаетесь на блок из блока, то тот блок будет скопирован.