【问题标题】:What does the "__block" keyword mean?“__block”关键字是什么意思?
【发布时间】:2011-10-28 04:45:36
【问题描述】:

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

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

【问题讨论】:

  • 检查here,以及“块和变量”部分。
  • @Code Monkey:我专门询问的是关键字,而不是一般的语法。所以不要认为它真的是重复的。
  • @Code Monkey:不,这不是重复的。你提到的问题根本没有提到__block
  • 如果有人想知道 Objective-C 的 __block 应该如何转换为 Swift:”闭包 [在 Swift 中] 具有与 [在 Objective-C 中] 块相似的捕获语义,但在一个方面有所不同关键方式:变量是可变的而不是复制的。换句话说,Objective-C 中 __block 的行为是 Swift 中变量的默认行为。” 来自 Apple 的书:Using Swift with Cocoa and Objective-C (Swift 2.2)。

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


【解决方案1】:

它告诉编译器任何被它标记的变量在块内使用时都必须以特殊方式处理。通常,块中也使用的变量及其内容会被复制,因此对这些变量所做的任何修改都不会显示在块之外。当它们被标记为__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 在调用块之前都被修改了。但是,在块内部,由于__block 关键字,只有对localCharacter 的修改可见。反之,区块可以修改localCharacter,而且这个修改在区块外是可见的。

【讨论】:

  • 优秀、简洁的解释和非常有用的示例。谢谢!
  • aBlock如何修改localCounter?它似乎只修改了 CounterGlobal。谢谢
  • 它不会修改localCounter,但会修改localCharacter。另外,请注意块中 localCounter 的值:它是 42,即使变量在之前调用块但之后块被创建(那是值被“捕获”的时候)。
  • 这是一个有用的解释——不过——你能解释一下你的解释中似乎矛盾的陈述吗?您在上面说“aBlock 修改 ...localCounter”,然后在 cmets 中您说“[aBlock] 不修改 localCounter。”它是哪一个?如果它是“未修改”,那么您的答案是否应该被编辑?
  • 一般来说,没有 __block 的 var 会在创建块时按值捕获并打包到块的“环境”中。但是 __block 变量不会被捕获,无论何时在块内部或外部使用它们时,都会按原样引用它们。
【解决方案2】:

@bbum 涵盖了blog post 中的深度块,并涉及到 __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;

【讨论】:

  • 有关保留周期问题的更多信息,请参阅此帖子:benscheirman.com/2012/01/…。在这种特定情况下,__weak 也足够了吗?也许更清楚一点......
  • 最后,__block 可用于避免强引用循环(也称为保留循环)的说法在 ARC 上下文中是完全错误的。由于 ARC 中的 __block 导致变量被强引用,实际上更有可能导致它们。stackoverflow.com/a/19228179/189006
【解决方案3】:

通常当你不使用 __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】:

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

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

    2. 在 MRC 中,__block 可用于避免保留块捕获的对象变量。小心这对 ARC 不起作用。在 ARC 中,您应该改用 __weak

    您可以参考apple doc了解详细信息。

    【讨论】:

      【解决方案5】:

      __block 是一种存储类型,用于使范围内的变量可变,更坦率地说,如果您使用此说明符声明变量,则其引用将传递给非只读块复制了解更多详情,请参阅Blocks Programming in iOS

      【讨论】:

        【解决方案6】:

        希望对你有帮助

        假设我们有这样的代码:

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

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

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

        【讨论】:

          【解决方案7】:

          来自Block Language Spec

          除了新的 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 节。

          【讨论】:

          • 你的链接都失效了
          • 这不是一个真正的答案,可以充实或删除。谢谢!
          【解决方案8】:

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

          【讨论】:

            猜你喜欢
            • 2011-02-14
            • 2010-10-29
            • 1970-01-01
            • 2010-09-12
            • 1970-01-01
            相关资源
            最近更新 更多