Что означает ключевое слово «__block»?

407

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

  • Что именно он сообщает компилятору?
  • Делает ли что-нибудь еще?
  • Если это все, что он делает, то почему это необходимо в первую очередь?
  • Это в документах где угодно? (Я не могу найти его).
  • 2
    проверьте здесь и раздел «Блоки и переменные».
Показать ещё 4 комментария
Теги:
objective-c-blocks

8 ответов

464
Лучший ответ

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

Для примера и дополнительной информации см. Тип хранилища __block в разделах программирования Apple Blocks.

Важным примером является следующий:

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, и эта модификация видна вне блока.

  • 7
    Отличное, краткое объяснение и очень полезный пример. Спасибо!
  • 1
    Как aBlock изменяет localCounter? Это только кажется, чтобы изменить CounterGlobal. Спасибо
Показать ещё 3 комментария
24

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

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

Подобно статическому, автоматическому и неустойчивому, __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;
  • 0
    См. Этот пост для получения дополнительной информации о проблеме сохранения цикла: benscheirman.com/2012/01/… . __weak ли __weak в этом конкретном случае? Это немного яснее, возможно ...
  • 15
    Наконец, утверждение о том, что __block может использоваться для избежания сильных ссылочных циклов (иначе говоря, сохраняющих циклов), совершенно неверно в контексте ARC. Из-за того факта, что в ARC __block вызывает сильную ссылку на переменную, на самом деле это более вероятно вызывает их. stackoverflow.com/a/19228179/189006
8

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

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

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

Подробную информацию можно найти в apple doc.

5

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

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

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

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

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

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

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

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

2

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

пусть мы имеем такой код, как:

{
     int stackVariable = 1;

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

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

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

2

Из Спецификация языка блока:

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

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

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

  • 0
    Ваши ссылки мертвы
0

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню