“ __block”关键字是什么意思?


445

__blockObjective-C 中的关键字到底是什么意思?我知道它允许您修改块内的变量,但我想知道...

  1. 它到底告诉编译器什么?
  2. 它还有其他作用吗?
  3. 如果仅此而已,那么为什么首先需要它?
  4. 它在文档中的任何地方吗?(我找不到)。

3
检查此处,以及“块和变量”部分。


1
@Code Monkey:我是在专门询问关键字,而不是一般的语法。因此,不要认为它确实是重复的。
— mjisrawi 2011年

3
@Code Monkey:不,这不是重复的。您提到的问题根本没有提及__block。
— DarkDust

3
而且,如果有人想知道Objective-C __block应该如何转换为Swift:“ [Swift]中的闭包与[Objective-C]中的块具有相似的捕获语义,但是在一个关键方面有所不同:变量是可变的,而不是复制的。换句话说,Objective-C中__block的行为是Swift中变量的默认行为。” 摘自Apple的书:将Swift与Cocoa和Objective-C结合使用(Swift 2.2)。
— 加里·凯纳嫩(JariKeinänen)

Answers:


542

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

有关示例和更多信息,请参见Apple的Blocks编程主题中的__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'
}

在此示例中,在调用该块之前修改了localCounter和localCharacter。但是,localCharacter由于使用了__block关键字,因此在块内部,仅对的修改是可见的。相反,该块可以修改,localCharacter并且该修改在块外部可见。


8
出色,简洁的说明,是一个非常有用的示例。谢谢!
— 伊万·斯通

1
aBlock如何修改localCounter?它似乎只修改CounterGlobal。谢谢
— CommaToast

8
它不会修改localCounter,但是会修改localCharacter。另外,还要注意localCounter该块中的值:即使变量在调用该块之前但在创建该块之后增加了(也就是“捕获”该值的时候),它的值为42 。
— DarkDust

1
但这是一个有用的解释-您能解释一下解释中似乎矛盾的陈述吗?您在上面说“ aBlock修改... localCounter”,然后在注释中说“ [aBlock]不修改localCounter”。哪有 如果“未修改”,那么您的答案应该被编辑吗?
— Praxiteles

2
通常,在创建该块时,不带__block的var将按值捕获并打包到该块的“环境”中。但是__block vars不会被捕获,无论何时在块的内部或外部使用它们,都按原样引用它们。
— jchnxu 2014年

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
最后,在ARC上下文中,声称__block可以避免使用强参考循环(又称为保留循环)的说法是完全错误的。由于在ARC __block中导致强烈引用该变量,因此实际上更可能导致它们。stackoverflow.com/a/19228179/189006
— 克里希南2014年

10

通常,当您不使用__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"

8

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

  1. 标记变量位于原始变量的词法范围与该范围内声明的任何块之间共享的存储中。然后clang将生成一个表示该变量的结构,并按引用(而不是按值)使用该结构。

  2. 在MRC中,__ block可用于避免保留块捕获的对象变量。请注意,这不适用于ARC。在ARC中,应改为使用__weak。

您可以参考apple doc以获得详细信息。


6

__block是一种存储类型,用于使范围变量可变,坦率地说,如果您使用此说明符声明变量,则其引用将传递给块而不是只读副本以获取更多详细信息,请参阅iOS块编程


2

希望这能够帮到你

假设我们有如下代码:

{
     int stackVariable = 1;

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

它将产生类似“变量不可分配”的错误,因为默认情况下该块内部的堆栈变量是不可变的。

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


1

从块语言规范:

除了新的Block类型,我们还为局部变量引入了一个新的存储限定符__block。[testme:块文字中的__block声明] __block存储限定符与现有的本地存储限定符auto,register和static互斥。[testme] __block限定的变量的行为就像它们在分配的存储中一样,并且此存储为上次使用所述变量后自动恢复。一种实现可以选择一种优化,其中存储最初是自动的,并且仅在引用块的Block_copy时才“移动”到分配的(堆)存储。这样的变量可以像普通变量一样被突变。

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

有关__block变量应编译为的内容的详细信息,请参见Block Implementation Spec第2.3节。


您的链接都死了
— Ben Leggiero

这并不是真正的答案,可以充实或删除。谢谢!
— Dan Rosenstark

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.