Objective-C 中的 __block
关键字究竟是什么意思?我知道它允许您修改块内的变量,但我想知道......
它到底告诉编译器什么?它还有其他作用吗?如果这就是它的全部作用,那么为什么首先需要它呢?它在任何地方的文档中吗? (我找不到它)。
__block
。
__block
应该如何转换为 Swift:”闭包 [在 Swift 中] 与块 [在 Objective-C 中] 具有相似的捕获语义,但在一个关键方面有所不同:变量是可变的而不是复制。换句话说,Objective-C 中 __block 的行为是 Swift 中变量的默认行为。” 来自 Apple 的书:Using Swift with Cocoa and Objective-C (Swift 2.2)。
它告诉编译器,任何由它标记的变量在块内使用时都必须以特殊方式处理。通常,块中也使用的变量及其内容会被复制,因此对这些变量所做的任何修改都不会显示在块之外。当它们被标记为 __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'
}
在此示例中,localCounter
和 localCharacter
都在调用块之前进行了修改。但是,在块内部,由于 __block
关键字,只有对 localCharacter
的修改可见。相反,该块可以修改 localCharacter
,并且此修改在该块之外是可见的。
@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;
__weak
是否也足够?或许更清楚一点...
当您不使用 __block 时,块会复制变量(按值调用),因此即使您在其他地方修改变量,块也不会看到更改。
__block 使块保持对变量的引用(按引用调用)。
NSString* str = @"hello";
void (^theBlock)() = ^void() {
NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"
在这两种情况下,您需要 __block:
如果要修改块内的变量并希望它在块外可见: __block NSString* str = @"hello"; void (^theBlock)() = ^void() { str = @"你好吗"; };块(); NSLog(@"%@", str); //打印“你好吗” 如果你想在声明块后修改变量并且你希望块看到变化: __block NSString* str = @"hello"; void (^theBlock)() = ^void() { NSLog(@"%@", str); }; str = @"你好吗";块(); //打印“你好吗”
__block 是一个存储限定符,可以通过两种方式使用:
标记一个变量存在于原始变量的词法范围和在该范围内声明的任何块之间共享的存储中。而 clang 将生成一个结构来表示这个变量,并通过引用(而不是按值)使用这个结构。在 MRC 中,__block 可用于避免保留块捕获的对象变量。小心这对 ARC 不起作用。在 ARC 中,您应该改用 __weak。
您可以参考apple doc了解详细信息。
希望对你有帮助
假设我们有这样的代码:
{
int stackVariable = 1;
blockName = ^()
{
stackVariable++;
}
}
它将给出类似“变量不可分配”的错误,因为块内的堆栈变量默认情况下是不可变的。
在声明之前添加 __block(storage modifier) 使其在块内可变,即 __block int stackVariable=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 节。
这意味着它作为前缀的变量可在块中使用。
不定期副业成功案例分享
localCounter
,但会修改localCharacter
。另外,请注意块中localCounter
的值:它是 42,即使变量在 在调用块之前但在创建块之后之后增加(那是价值被“捕获”的时候)。