【问题标题】:How does Objective-C block capture a non-object value?Objective-C 块如何捕获非对象值?
【发布时间】:2016-10-29 13:14:51
【问题描述】:
int anInteger = 42;

void (^testBlock)(void) = ^{
    NSLog(@"Integer is: %i", anInteger);
};

anInteger = 84;

testBlock();

Integer is: 42

这是来自 Apple 官方指南的示例。

现在,对于对象值,很容易理解,它保持对它的引用。所以后来,当它的原始引用更改为指向其他东西时,或者只是被破坏了。这个引用还在,所以引用计数不会为零,会保留原来的值。

但是,对于上面的示例代码,它不是一个对象。该块保留对它的引用,然后值更改为 84。我想这是对自身的更改而不是其副本,这意味着指针指向的值已更改。怎么还是42?

【问题讨论】:

标签: objective-c


【解决方案1】:

来自文档的Blocks and Variables 部分:

以下规则适用于块内使用的变量:

  1. 全局变量是可访问的,包括存在于封闭词法范围内的静态变量。
  2. 传递给块的参数是可访问的(就像函数的参数一样)。
  3. 封闭词法范围的本地堆栈(非静态)变量被捕获为 const 变量。 它们的值取自程序中块表达式的位置。在嵌套块中,值是从最近的封闭范围捕获的。
  4. 使用 __block 存储修饰符声明的封闭词法范围的局部变量由引用提供,因此是可变的。 任何更改都会反映在封闭词法范围中,包括在同一封闭词法范围内定义的任何其他块。这些在 __block 存储类型中有更详细的讨论。
  5. 在块的词法范围内声明的局部变量,其行为与函数中的局部变量完全相同。 块的每次调用都会提供该变量的新副本。这些变量又可以用作包含在块中的块中的 const 或引用变量。

规则 3 适用于您问题中的代码。

【讨论】:

    【解决方案2】:

    块引入了必要的间接性以确保发生这种情况。看起来是本地的但被块捕获的变量实际上是由编译器在堆上分配的。除其他事项外,这对于该变量能够超过声明它的函数的生命周期是必要的。

    【讨论】:

    • 这不是原因,编译器会检测一个块是否可以离开捕获变量的范围。复制的原因——可以保存在堆栈或堆上——是,这是闭包的含义。
    【解决方案3】:

    长话短说:复制整数值。 (更准确地说:结构和对象引用也被复制。但在对象引用的情况下,它是一个引用。)

    顺便说一句:这就是闭包的意思。这就是封闭的目的。它们存在的原因。您想要正是这种行为。否则,您必须确保在块运行时不会更改值 - 可能是几秒钟或几分钟后。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多