Что означает ключевое слово "__block"?
Что означает ключевое слово __block
в Objective-C? Я знаю, что он позволяет изменять переменные в блоках, но я хотел бы знать...
- Что именно он сообщает компилятору?
- Делает ли что-нибудь еще?
- Если это все, что он делает, то почему это необходимо в первую очередь?
- Это в документах где угодно? (Я не могу найти его).
Ответы
Ответ 1
Он сообщает компилятору, что любая переменная, помеченная им, должна обрабатываться специальным образом, когда она используется внутри блока. Обычно переменные и их содержимое, которые также используются в блоках, копируются, поэтому любая модификация этих переменных не отображается за пределами блока. Когда они помечены знаком __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
, и эта модификация видна вне блока.
Ответ 2
@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;
Ответ 3
__ блок - это определитель хранилища, который можно использовать двумя способами:
-
Отмечает, что переменная живет в хранилище, которое разделяется между лексической областью исходной переменной и любыми блоками, объявленными в этой области. И clang будет генерировать структуру для представления этой переменной и использовать эту структуру по ссылке (не по значению).
-
В MRC __ блок можно использовать, чтобы избежать сохранения переменных объекта, захваченных блоками. Осторожно, что это не работает для ARC. В ARC вы должны использовать __ слабый.
Подробную информацию можно найти в apple doc.
Ответ 4
Обычно, когда вы не используете __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.
Ответ 6
Из спецификации языка блока:
В дополнение к новому типу блока мы также вводим новый спецификатор хранения __block для локальных переменных. [testme: объявление __block внутри литерала блока] Квалификатор хранилища __block является взаимоисключающим для существующих локальных спецификаторов хранения auto, register и static. [testme] Переменные, квалифицированные __block, действуют так, как если бы они находились в выделенном хранилище, и это хранилище автоматически восстанавливается после последнего использования указанной переменной. Реализация может выбрать оптимизацию, когда хранилище изначально автоматическое и только "перемещается" в выделенное (кучное) хранилище после Block_copy ссылающегося блока. Такие переменные могут быть видоизменены как обычные переменные.
В случае, когда переменная __block является Блоком, необходимо предположить, что переменная __block находится в выделенном хранилище и, как таковая, должна ссылаться на Блок, который также находится в выделенном хранилище (что это является результатом операции Block_copy). Несмотря на это, нет никаких условий для выполнения Block_copy или Block_release, если реализация обеспечивает начальное автоматическое хранение блоков. Это связано с тем, что несколько потоков пытаются обновить совместно используемую переменную, а также необходимость синхронизации при удалении старых значений и копировании новых. Такая синхронизация выходит за рамки данной языковой спецификации.
Для получения подробной информации о том, к чему должна компилироваться переменная __block, см. Спецификацию реализации блока, раздел 2.3.
Ответ 7
надеюсь, что это поможет вам
пусть мы имеем такой код, как:
{
int stackVariable = 1;
blockName = ^()
{
stackVariable++;
}
}
он выдаст ошибку, например, "переменная не назначается", потому что переменная стека внутри блока по умолчанию неизменяема.
добавление __block (модификатор хранилища) перед объявлением делает его изменяемым внутри блока i.e __block int stackVariable=1;
Ответ 8
Это означает, что переменная, являющаяся префиксом, доступна для использования внутри блока.