Objective c 修改调度队列内父作用域中的数据

Objective c 修改调度队列内父作用域中的数据,objective-c,Objective C,根据并发编程指南: 将块添加到调度队列时,这些值通常必须以只读格式保留。但是,同步执行的块也可以使用预先添加了_block关键字的变量将数据返回到父级的调用范围 我一直在修改队列外创建的变量,但我从来没有用\uu block指定它们,所以我想知道到底什么时候或者为什么需要这样做。或者实例变量总是可以通过块进行固有的更改,就好像从幕后为它们分配了\u block 更新:我还应该补充一点,我使用的是异步队列,而上面提到的变量只能在同步队列中更改(使用\uu block)访问块内类的实例变量iVar,

根据并发编程指南:

将块添加到调度队列时,这些值通常必须以只读格式保留。但是,同步执行的块也可以使用预先添加了_block关键字的变量将数据返回到父级的调用范围

我一直在修改队列外创建的变量,但我从来没有用
\uu block
指定它们,所以我想知道到底什么时候或者为什么需要这样做。或者实例变量总是可以通过块进行固有的更改,就好像从幕后为它们分配了
\u block


更新:我还应该补充一点,我使用的是异步队列,而上面提到的变量只能在同步队列中更改(使用
\uu block

访问块内类的实例变量
iVar
,编译器将其解释为
self->iVar
。因此,块捕获未修改的
自身

我确信
\u块
修饰符也适用于
dispatch\u async
,因此这可能是文档错误

已添加

以下示例显示了如何将
\u块
变量与
调度\u异步
一起使用:

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编程指南中找到了这个。它解释了为什么对异步块使用
\uu block
变量没有问题

__块变量存在于变量的词法作用域与声明或声明的所有块和块副本之间共享的存储器中 在变量的词法范围内创建。因此,存储将 如果存在块的任何副本,则在堆栈帧被破坏后仍然存在 在帧内声明在帧结束后继续存在(对于 例如,在某处排队等待稍后执行)。倍数 给定词法作用域中的块可以同时使用共享 变数

作为一种优化,块存储从 就像块本身一样堆叠。如果块是使用 块拷贝(或在Objective-C中发送块拷贝时), 变量被复制到堆中。因此,一个_块的地址 变量可以随时间变化


引用《并发编程指南》是关于在函数或方法内部创建的变量。在它们内部创建的变量是在堆栈上创建的,该堆栈保存它们的时间与函数调用的时间一样长。换句话说,当函数返回时,它的堆栈帧将与变量一起销毁

Objective-c中的对象是在堆上创建的,因此它们可以在函数返回后生存,但是当您创建类似以下的行时:

MyClass *object = [[MyClass alloc] init];
您正在创建将放置在堆上的对象,但您也在创建变量
object
,该变量保存指向堆中对象的指针。该变量放置在当前方法/函数的堆栈框架上,并将在返回后处理。若不释放对象,即使函数已结束,也不会释放对象

这就是块复制块内引用的父范围变量的原因。它们被复制到块的私有内存中,因为它们可能在函数调用结束时被销毁,正如您所知,块可以在调用结束后使用。这就是为什么您需要使用
\u block
说明符来通知block不要从堆栈复制变量并直接使用它。由于该变量在函数返回时可能会被销毁,因此并发编程指南指出,在这种情况下,只应使用同步分派,因为它将确保在函数返回之前执行块编辑:正如Martin R正确指出的那样,
\u block
变量并不总是直接从堆栈中使用。如上所述,它们被视为指针,当复制块时(如在分派中),它们的地址可以从堆栈中更改。复制的变量放在块和函数之间共享的存储器中,以延长变量的生存期。我认为,之所以说局部变量应该与dispatch_sync一起使用,是因为在非ARC环境中,变量不会被保留,从而造成使用deallocate对象的危险。出于同样的原因,我认为在静态变量和实例变量之前不使用_块。


属于实例变量静态变量的变量不存在于任何函数的堆栈上,因此它们不按块复制,也不需要
\u块
说明符。静态变量将在程序运行时一直存在,但当保存该变量的对象被销毁时,实例变量将被销毁。这就是为什么块有另一个特性——块内引用的所有对象都将保留一个块的生命周期,以确保对象不会意外地被释放。

中的it声明实例变量在块中可用,当然它们可能会被使用。文档并不是说你不能!对ivar对象使用
\uu block
是不安全的,因为如果它们有这个说明符,它们就不会被保留。@Johnnywho:andrewx问为什么访问块内的实例变量不需要
\uu block
修饰符,我解释了这一点。他还询问是否可以使用
\uu block
,哪些异步块与文档相反。我说可以用,并举例说明。所以我不知道为什么这个答案被否决了当然,我可能是错的,但是我非常确定在这里使用
\uu block
变量没有问题。我没有看到一个变量可能不被保留的情况(当然假设为弧)