Что означает ключевое слово "__block"? - PullRequest
427 голосов
/ 16 августа 2011

Что именно означает ключевое слово __block в Objective-C?Я знаю, что это позволяет вам изменять переменные внутри блоков, но я хотел бы знать ...

  1. Что именно он говорит компилятору?
  2. Делает ли он что-нибудь еще?
  3. Если это все, что нужно, то зачем это вообще нужно?
  4. Это где-нибудь в документах?(Я не могу его найти).

Ответы [ 8 ]

519 голосов
/ 16 августа 2011

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

Пример и дополнительную информацию см. Тип хранения __block в Apple * 1006.* Темы программирования блоков .

Вот важный пример:

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'
}

В этом примере и localCounter, и localCharacter модифицируются перед вызовом блока.Однако внутри блока будет видна только модификация localCharacter благодаря ключевому слову __block.И наоборот, блок может изменить localCharacter, и эта модификация видна за пределами блока.

27 голосов
/ 16 августа 2011

@ bbum подробно описывает все блоки в сообщении в блоге и касается типа хранилища __block.

__ - это отдельный тип хранилища

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

Что касается вариантов использования, вы обнаружите, что __block иногда используется, чтобы избежать сохранения циклов, поскольку он не сохраняет аргумент.Типичным примером является использование self.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
8 голосов
/ 16 июля 2015

__ block - это спецификатор хранилища, который можно использовать двумя способами:

  1. Отмечает, что переменная находится в хранилище, которое совместно используется лексической областью действия:исходная переменная и любые блоки, объявленные в этой области.И clang сгенерирует структуру для представления этой переменной и использует эту структуру по ссылке (не по значению).

  2. В MRC можно использовать __ block , чтобы избежатьсохранить переменные объекта, которые захватывает блок.Осторожно, это не работает для ARC.В ARC вы должны использовать __ слабых .

Для получения подробной информации см. Apple DOC .

7 голосов
/ 01 ноября 2016

Обычно, когда вы не используете __block, блок копирует (сохраняет) переменную, поэтому, даже если вы измените переменную, блок имеет доступ к старому объекту.

NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"

В этих 2 случаях вам нужно __block:

1.Если вы хотите изменить переменную внутри блока и ожидать, что она будет видна снаружи:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    str = @"how are you";
};
theBlock();
NSLog(@"%@", str); //prints "how are you"

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

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints "how are you"
5 голосов
/ 26 марта 2012

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

2 голосов
/ 01 июля 2014

надеюсь, что это поможет вам

предположим, у нас есть код вроде:

{
     int stackVariable = 1;

     blockName = ^()
     {
      stackVariable++;
     }
}

выдаст ошибку типа «переменная не присваивается», потому что переменная стека внутри блока по умолчанию неизменна.

добавление __block (модификатор хранилища) перед объявлением делает его изменяемым внутри блока, т.е. __block int stackVariable=1;

2 голосов
/ 16 августа 2011

Из спецификации языка блокировки :

В дополнение к новому типу блока мы также вводим новый спецификатор хранения __block для локальных переменных. [testme: объявление __block внутри литерала блока] Квалификатор хранилища __block является взаимоисключающим с существующими квалификаторами локального хранилища auto, register и static. [testme] Переменные, квалифицированные с помощью __block, действуют так, как если бы они находились в выделенном хранилище, и это хранилище автоматически восстанавливается после последнего использования указанной переменной. Реализация может выбрать оптимизацию, когда хранилище изначально автоматическое и только «перемещается» в выделенное (кучное) хранилище после Block_copy ссылающегося блока. Такие переменные могут быть видоизменены как обычные переменные.

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

Подробнее о том, для чего должна компилироваться переменная __block, см. Спецификация реализации блока , раздел 2.3.

0 голосов
/ 21 сентября 2018

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

...