Objective-C 中“__block”关键字是什么意思?

Objective-C中的__block关键字到底是什么意思?我知道它允许你在块内修改变量,但我想知道…

  1. 它到底告诉编译器什么?
  2. 它还有别的功能吗?
  3. 如果这就是它所做的一切,那么为什么首先需要它呢?
  4. 文档里有吗?(我找不到)。
117356 次浏览

@bbum覆盖了博客中的深度块,并涉及到__block存储类型。

__block是一种独特的存储类型

就像static、auto和volatile一样,__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;

它告诉编译器,当它在块中使用时,任何被它标记的变量都必须以特殊的方式处理。通常情况下,在块中使用的变量及其内容会被复制,因此对这些变量所做的任何修改都不会在块外显示。当它们被标记为__block时,在块内部所做的修改在块外部也可见。

有关示例和更多信息,请参见Apple的模块编程主题中的__block存储类型

一个重要的例子是:

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

在这个例子中,localCounterlocalCharacter在调用块之前都被修改了。然而,在块内部,由于__block关键字,只有对localCharacter的修改是可见的。相反,该块可以修改localCharacter,并且该修改在块外部可见。

块语言规范:

除了新的Block类型,我们还为局部变量引入了一个新的存储限定符__block。__block存储限定符与现有的本地存储限定符auto、register和static互斥。[testme] __block限定的变量就像它们在分配的存储中一样,并且在上次使用该变量后,该存储将自动恢复。实现可以选择一种优化,即存储最初是自动的,并且只“移动”;在引用块的Block_copy上分配(堆)存储。这些变量可以像正常变量一样发生突变。

在__block变量是Block的情况下,必须假定__block变量位于已分配的存储中,因此假定引用的Block也位于已分配的存储中(即它是Block_copy操作的结果)。尽管如此,如果实现为块提供初始自动存储,则没有规定要执行Block_copy或Block_release。这是由于潜在的几个线程试图更新共享变量时存在固有的竞争条件,并且需要围绕处理旧值和复制新值进行同步。这种同步超出了本语言规范的范围。

有关__block变量应该编译为什么的详细信息,请参见2.3节中的块实现规范

__block是一种用于使作用域内变量可变的存储类型,更坦率地说,如果你用这个说明符声明一个变量,它的引用将被传递给块而不是只读副本,详情请参阅iOS中的编程块

希望这对你有所帮助

让我们假设我们有这样一个代码:

{
int stackVariable = 1;


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

它会给出一个类似“变量不可赋值”的错误,因为块内的堆栈变量默认是不可变的。

在它的声明之前添加__block(存储修饰符),使它在块内可变,即__block int stackVariable=1;

__block是一个存储限定符,可以以两种方式使用:

  1. 标记变量位于原始变量的词法作用域和在该作用域中声明的任何块之间共享的存储中。clang将生成一个表示该变量的结构体,并通过引用(而不是值)使用该结构体。

  2. 在MRC中,__block可用于避免保留块捕获的对象变量。注意这对ARC不起作用。在ARC中,你应该使用__weak代替。

你可以参考苹果医生获取详细信息。

当你不使用__block时,块会复制变量(按值调用),所以即使你在其他地方修改了变量,块也看不到变化。

__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"
    

这意味着它作为前缀的变量可以在块中使用。