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

时间:2011-08-16 15:39:53

标签: objective-c ios objective-c-blocks

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

  1. 它究竟是什么告诉编译器?
  2. 它还能做什么吗?
  3. 如果就是这样,那么为什么首先需要呢?
  4. 是否在文档中随处可见? (我找不到)。

8 个答案:

答案 0 :(得分:513)

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

有关示例和更多信息,请参阅Apple的 Blocks Programming Topics 中的The __block Storage Type

重要的例子就是这个:

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都会在调用块之前进行修改。但是,在块中,由于localCharacter关键字,只能看到对__block的修改。相反,该块可以修改localCharacter,并且此修改在块外可见。

答案 1 :(得分:26)

@bbum涵盖blog post中的深度块,并触及__block存储类型。

  

__ block是一种独特的存储类型

     

就像静态,自动和易失性一样,__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;

答案 2 :(得分:8)

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

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

  2. 在MRC中, __ block 可用于避免块捕获保留对象变量。小心这对ARC不起作用。在ARC中,您应该使用 __ weak

  3. 有关详细信息,请参阅apple doc

答案 3 :(得分:7)

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

答案 4 :(得分:5)

__block 是一种存储类型,用于使范围变量可变,更坦率地说,如果使用此说明符声明变量,其引用将传递给块而不是读取 - 只需复制以获取更多详细信息,请参阅Blocks Programming in iOS

答案 5 :(得分:2)

来自Block Language Spec

  

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

     

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

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

答案 6 :(得分:2)

希望这会对你有所帮助

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

{
     int stackVariable = 1;

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

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

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

答案 7 :(得分:0)

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