根据并发编程指南:
将块添加到调度队列时,这些值通常必须以只读格式保留。但是,同步执行的块也可以使用带有 __block 关键字的变量将数据返回到父级的调用范围。
我一直在更改在队列之外创建的变量,但我从未指定它们,__block
所以我想知道何时或为什么需要这样做。还是实例变量总是可以通过块固有地改变,就好像它们是__block
从幕后分配给它们的?
更新:我也应该补充一点,我正在使用异步队列,而上面说变量只能在同步队列中更改(使用__block
)
根据并发编程指南:
将块添加到调度队列时,这些值通常必须以只读格式保留。但是,同步执行的块也可以使用带有 __block 关键字的变量将数据返回到父级的调用范围。
我一直在更改在队列之外创建的变量,但我从未指定它们,__block
所以我想知道何时或为什么需要这样做。还是实例变量总是可以通过块固有地改变,就好像它们是__block
从幕后分配给它们的?
更新:我也应该补充一点,我正在使用异步队列,而上面说变量只能在同步队列中更改(使用__block
)
在块内访问类的实例变量iVar
被编译器解释为self->iVar
. 因此块捕获self
,它没有被修改。
我确信__block
修饰符也适用于dispatch_async
,因此这可能是文档错误。
添加
以下示例显示了如何将__block
变量与 一起使用dispatch_async
:
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
__block int total = 0;
printf("address of total: %p\n", &total);
// dispatch some (concurrent) blocks asynchronously:
dispatch_async(queue, ^{
OSAtomicAdd32(5, &total);
});
dispatch_async(queue, ^{
OSAtomicAdd32(7, &total);
});
// Wait for all blocks to complete:
dispatch_barrier_sync(queue, ^{ });
printf("address of total: %p\n", &total);
printf("total=%d\n", total);
输出:
address of total: 0x7fff5fbff8f0
address of total: 0x100108198
total=12
可以看到total
在执行块时从堆栈复制到堆。
添加
我刚刚在Blocks Programming Guide中找到了这个。它解释了为什么在__block
异步块中使用变量是没有问题的。
__block 变量存在于变量的词法范围和在变量的词法范围内声明或创建的所有块和块副本之间共享的存储中。因此,如果在帧内声明的块的任何副本在帧结束后仍然存在(例如,通过在某处排队以供以后执行),则存储将在堆栈帧的破坏中幸存下来。给定词法范围内的多个块可以同时使用一个共享变量。
作为一种优化,块存储从堆栈开始——就像块本身一样。如果块是使用 Block_copy 复制的(或者在 Objective-C 中,当块被发送一个副本时),变量被复制到堆中。因此,__block 变量的地址可以随时间改变。
来自并发编程指南的引用是关于在函数或方法中创建的变量。在它们内部创建的变量是在堆栈上创建的,只要函数调用就保存它们。换句话说,当函数返回时,它的堆栈帧与变量一起被销毁。
Objective-c 中的对象是在堆上创建的,因此它们可以在函数返回后存活,但是当您创建如下行时:
MyClass *object = [[MyClass alloc] init];
您正在创建将放置在堆上的对象,但您也在创建object
将指向堆中对象的指针的变量。该变量被放置在当前方法/函数的堆栈框架上,并将在它返回后被释放。你的对象,如果你不释放它,即使功能已经结束,也不会被释放。
这就是为什么块复制在块内引用的父范围变量的原因。它们被复制到块的私有内存中,因为它们可能在函数调用结束时被销毁,并且如您所知,块可以在那之后使用。这就是为什么您需要使用 编辑:正如 Martin R 正确指出的那样,__block
说明符来通知块不要从堆栈中复制变量并直接使用它。因为这个变量可以在函数返回时被销毁,并发编程指南指出这种情况只能使用同步调度,因为它会确保在函数返回之前执行块。__block
变量并不总是直接从堆栈中使用。作为本文档说,它们被视为指针,并且它们的地址可以在复制块时从堆栈中更改(如在调度中)。复制的变量被放置在块和函数之间共享的存储中,以延长变量的生命周期。我认为声明局部变量应该与 dispatch_sync 一起使用的原因是在非 ARC 环境变量中不保留*,从而造成使用释放对象的危险。出于同样的原因,我认为您不要在静态变量和实例变量之前使用 __block 。
Variables that are instance variables or static variables don't live on the stack of any function, thus they're not copied by block, and you don't need __block
specifier. Static variable will live as long as program runs, but instance variable is destroyed when object which holds that variable is destroyed. That's why blocks have another feature - all objects that are referenced inside a block will be retained for lifetime of a block to ensure that object will not be deallocated unexpectedly.