Що означає ключове слово "__block"?


446

Що саме означає __blockключове слово в Objective-C? Я знаю, що це дозволяє змінювати змінні в блоках, але я хотів би знати ...

  1. Що саме це повідомляє компілятору?
  2. Чи робить це ще щось?
  3. Якщо це все, що це робиться, то навіщо це в першу чергу?
  4. Чи є в документах десь? (Я не можу його знайти).

3
перевірте тут , і розділ "Блоки та змінні".


1
@Code Monkey: Я питав конкретно про ключове слово, а не про синтаксис взагалі. Тому не думайте, що це дійсно дублікат.
mjisrawi

3
@Code Monkey: Ні, це не дублікат. Питання, про яке ви згадуєте, взагалі не говорить __block.
DarkDust

3
А якщо когось цікавить, як __blockслід перевести Objective-C на Swift: "Закриття [у Swift] має схожу семантику захоплення, як блоки [в Objective-C], але відрізняються одним ключовим способом: Змінні змінюються, а не копіюються. Іншими словами, поведінка __block в Objective-C є поведінкою за замовчуванням для змінних у Swift. " З книги Apple: Використання Swift з какао та Objective-C (Swift 2.2).
Jari Keinänen

Відповіді:


543

Він повідомляє компілятору, що будь-яка змінена ним змінна повинна бути оброблена особливим чином, коли вона використовується всередині блоку. Зазвичай змінні та їх вміст, які також використовуються в блоках, копіюються, тому будь-які модифікації цих змінних не відображаються поза блоком. Коли вони позначені __block, модифікації, зроблені всередині блоку, також видно поза ним.

Для прикладу та додаткової інформації див . Тип зберігання __block у Темах програмування блоків Apple .

Важливий приклад:

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і ця модифікація видно поза блоком.


8
Відмінне, стисле пояснення та дуже корисний приклад. Дякую!
Еван Стоун

1
Як aBlock змінює localCounter? Це, здається, лише модифікує CounterGlobal. Спасибі
CommaToast

8
Він не модифікує localCounter, але змінює localCharacter. Також зверніть увагу на значення, яке localCounterмає в блоці: це 42, навіть якщо змінна збільшується до виклику блоку, але після створення блоку (саме тоді значення отримало "захоплення").
DarkDust

1
Це корисне пояснення - хоч - чи можете ви пояснити, що у вашому поясненні здається суперечливим? Ви сказали вище, що "aBlock змінює ... localCounter", а потім у коментарях ви говорите, що "[aBlock] НЕ модифікує localCounter." Що це таке? Якщо він "не змінений", то чи слід редагувати вашу відповідь?
Праксітелес

2
Взагалі, vars без __block буде зафіксована за значенням і упакована у "середовище" блоку, коли блок буде створений. Але __block vars не буде зафіксовано, коли вони використовуються всередині або поза блоком, на них посилається як є.
jchnxu

27

@bbum охоплює глибинні блоки в публікації блогу та торкається типу зберігання __block.

__block - це окремий тип зберігання

Як і статичний, автоматичний і летючий, __block - це тип зберігання. Він повідомляє компілятору, що сховищем змінної слід керувати по-різному.

...

Однак для змінних __block блок не зберігається. Ви повинні утримувати та звільняти за потребою.
...

Щодо випадків використання, які ви знайдете __block, іноді використовується для уникнення збереження циклів, оскільки він не зберігає аргумент. Поширений приклад - використання самості.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;

Дивіться цю посаду для отримання додаткової інформації про проблему збереження циклу: benscheirman.com/2012/01 / ... . Було б __weakдостатньо і в цьому конкретному випадку? Можливо, трохи зрозуміліше ...
Харі Карам Сінгх

17
Нарешті, твердження про те, що __block можна використовувати для уникнення сильних еталонних циклів (він же зберігає цикли), є простим помилковим у контексті ARC. Через те, що в ARC __block викликає сильну посилання на змінну, насправді більш шанси викликати їх. stackoverflow.com/a/19228179/189006
Крішнан

10

Зазвичай, коли ви не використовуєте __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. Якщо ви хочете змінити змінну після того, як ви оголосили блок і очікуєте, що блок побачить зміни:

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

8

__block - це класифікатор пам’яті, який можна використовувати двома способами:

  1. Позначає, що змінна живе у сховищі, яке поділяється між лексичним діапазоном вихідної змінної та будь-якими блоками, оголошеними в межах цієї області. І clang буде генерувати структуру для представлення цієї змінної, і використовувати цю структуру за посиланням (не за значенням).

  2. У MRC __block можна використовувати, щоб уникнути збереження змінних об'єктів, який блок захоплює. Будьте уважні, що це не працює для ARC. У ARC вам слід використовувати __weak замість цього.

Для отримання детальної інформації можна звернутися до яблучного документа .


6

__blockце тип пам’яті, який використовується для того, щоб зміни змінних областей змінюватись, чесніше кажучи, якщо ви оголошуєте змінну за допомогою цього специфікатора, її посилання буде передано до блоків, а не для читання, копії для отримання більш детальної інформації див. Блокування програмування в iOS


2

сподіваюся, що це вам допоможе

припустимо, у нас є такий код, як:

{
     int stackVariable = 1;

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

це призведе до помилки типу "змінна не призначається", оскільки змінна стека всередині блоку за замовчуванням незмінна.

додавання __block (модифікатор зберігання) напередодні його оголошення робить його змінним всередині блоку, тобто __block int stackVariable=1;


1

З Spec Language Spec :

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

У випадку, коли змінна __block є Блоком, слід припустити, що змінна __block знаходиться у виділеному сховищі, і як таке вважається, що вона посилається на Блок, який також знаходиться у виділеному сховищі (що це результат операції Block_copy). Незважаючи на це, не передбачено робити Block_copy або Block_release, якщо реалізація забезпечує початкове автоматичне зберігання для Blocks. Це пов’язано з властивою умовою гонки потенційно декількох потоків, які намагаються оновити загальну змінну, та необхідністю синхронізації навколо розміщення старих значень та копіювання нових. Така синхронізація виходить за межі цієї мовної специфікації.

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


Ваші посилання загинули
Бен Леджіеро,

Це насправді не є відповіддю і може бути розроблено або видалено. Дякую!
Дан Розенстарк

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.