Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/98.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios Objective-C中的块是否始终保证捕获变量?_Ios_Objective C_Macos_Cocoa_Objective C++ - Fatal编程技术网

Ios Objective-C中的块是否始终保证捕获变量?

Ios Objective-C中的块是否始终保证捕获变量?,ios,objective-c,macos,cocoa,objective-c++,Ios,Objective C,Macos,Cocoa,Objective C++,在Objective-C(Objective-C++)中,编译器是否有条件检测到块中的变量捕获从未使用过,从而决定不首先捕获变量 例如,假设您有一个NSArray,其中包含大量项目,可能需要很长时间才能解除分配。您需要在主线程上访问NSArray,但一旦完成,您就愿意在后台队列中取消分配它。后台块只需要捕获阵列,然后立即解除分配。它实际上与它没有任何关系。编译器能否检测到这一点,并“错误地”跳过块捕获 例如: // On the main thread... NSArray *outgoingR

在Objective-C(Objective-C++)中,编译器是否有条件检测到块中的变量捕获从未使用过,从而决定不首先捕获变量

例如,假设您有一个
NSArray
,其中包含大量项目,可能需要很长时间才能解除分配。您需要在主线程上访问
NSArray
,但一旦完成,您就愿意在后台队列中取消分配它。后台块只需要捕获阵列,然后立即解除分配。它实际上与它没有任何关系。编译器能否检测到这一点,并“错误地”跳过块捕获

例如:

// On the main thread...
NSArray *outgoingRecords = self.records;
self.records = incomingRecords;

dispatch_async(background_queue, ^{
  (void)outgoingRecords;

  // After this do-nothing block exits, then outgoingRecords
  // should be deallocated on this background_queue.  
});
我是否保证
outgoingRecords
将始终在该块中捕获,并且它将始终在
后台队列中解除分配

编辑#1

我将添加更多的上下文来更好地说明我的问题:

我有一个Objective-C++类,它包含一个非常大的不可变记录的std::vector。这很可能是100多万条记录。它们是向量中的基本结构,在主线程上访问以填充表视图。在后台线程上,可能会将一组不同的数据库记录读入一个单独的向量,该向量也可能相当大

后台读取完成后,我跳转到主线程交换Objective-C对象并重新填充表

在这一点上,我根本不关心旧向量或其父Objective-C类的内容。没有什么奇特的析构函数或对象图可以拆卸,但释放数百兆字节甚至千兆字节的内存并不是瞬间的。所以我愿意把它放到后台队列中,并在那里进行内存释放。在我的测试中,这似乎工作得很好,在16毫秒过去之前,我在主线程上有更多的时间做其他事情

我试图理解我是否可以简单地在一个“空”块中捕获对象,或者我是否应该执行某种无操作操作(如call
count
),以便编译器无法以某种方式优化它

编辑#2

(我最初试图让问题尽可能简单,但似乎比这更微妙。根据Ken下面的回答,我将添加另一个场景。)

这是另一个不使用调度队列但仍然使用块的场景,这是我真正感兴趣的部分

id<MTLCommandBuffer> commandBuffer = ...

// A custom class that manages an MTLTexture that is backed by an IOSurface.
__block MyTextureWrapper *wrapper = ... 

// Issue some Metal calls that use the texture inside the wrapper.

// Wait for the buffer to complete, then release the wrapper.
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
  wrapper = nil;
}];
id commandBuffer=。。。
//管理由IOSurface支持的MTLTexture的自定义类。
__块MyTextureWrapper*包装器=。。。
//发出一些使用包装内纹理的金属调用。
//等待缓冲区完成,然后释放包装器。
[commandBuffer addCompletedHandler:^(id cb){
包装=零;
}];
在这种情况下,执行顺序由金属保证。与上面的示例不同,在此场景中,性能不是问题。相反,支持
MTLTexture
的IOSurface正在被回收到CVPixelBufferPool中。iSurface正在进程之间共享,据我所知,
MTLTexture
似乎不会增加该表面上的使用计数。我的包装器类可以。当我的包装器类被释放时,useCount被递减,然后缓冲池可以自由地回收IOSurface

这一切都如预期的那样工作,但我最终得到了上面这样愚蠢的代码,只是因为不确定是否需要在块中“使用”包装器实例以确保它被捕获。如果包装器在完成处理程序运行之前被解除分配,那么IOSurface将被回收,纹理将被覆盖

编辑以解决问题编辑: 从叮当声中:

化合物中引用的本地自动(堆栈)变量 块的语句作为常量导入并由块捕获 副本。捕获(绑定)在块执行时执行 字面表达式求值

如果可以证明,编译器不需要捕获变量 实际上不会计算对变量的引用。 程序员可以通过在 块开头的语句,如所示

(void) foo;
当捕获变量可能产生副作用时,这很重要 在Objective-C或C++中

(重点加上。)

请注意,使用此技术可以保证引用对象的寿命至少与块的寿命相同,但不能保证它将随块一起释放,也不能保证由哪个线程释放


无法保证提交到后台队列的块将是最后一个保存对数组的强引用的代码(甚至忽略块是否捕获变量的问题)

首先,该块实际上可能在提交它的上下文返回并释放其强引用之前运行。也就是说,调用
dispatch\u async()
的代码可以从CPU上交换,块可以首先运行


但是,即使块的运行时间稍晚,对数组的引用也可能在某个自动释放池中,并且在一段时间内不会释放。或者在其他地方可能有一个强引用,该引用最终将被清除,但不在您的明确控制下。

如果不使用该引用,为什么要更早地释放它呢?如果释放该数组需要很长时间,那么我更希望在后台队列而不是在主队列上执行该操作,我不确定在解除分配过程中会花费多少时间,但在类似的情况下,我会假设分配需要更多的时间。你有没有想过重用这些对象?数组