目标 C:线程之间的自动释放对象?
Posted
技术标签:
【中文标题】目标 C:线程之间的自动释放对象?【英文标题】:Objective C: Autoreleased objects between threads? 【发布时间】:2013-11-06 01:40:29 【问题描述】:如果我有一个自动释放的对象并且我需要将它提供给不同的线程,那么最好的方法是什么?
假设我有一个在线程 0 中自动释放的对象。我将这个对象告诉线程 1,它会保留它,因为它需要它。稍后它完成,它释放它。没问题。当线程 0 再次运行并清空其自动释放池时,它看到保留计数为 1,并且因为它是一个自动释放的对象,所以它会释放。一切都很好,因此线程无关紧要。对吧?
顺便说一句,这最初是一个面试问题。面试官坚持认为不能将自动释放的对象提供给另一个线程。他似乎对此几乎生气了。在技术面试中,我越来越多地遇到相信自己无所不知的人。
【问题讨论】:
将其传递到保留/强变量中。 最好的方法是使用 ARC 和 GCD。 @Bryan ... 为什么选择 GCD?似乎是多余的。 所以你不需要管理线程 “它看到保留计数是 1,并且因为它是一个自动释放的对象,所以它会释放”——有点误导。池在最后释放它,“dealloc”部分确实在保留计数为零时运行。 【参考方案1】:您不应该将自动释放的对象直接传递给其他线程。
在这段代码中
id _sharedVariable; // ivar
NSConditionLock *_lock;
- (void)thread1
id objectNeedToPass = [[NSObject new] autorelease];
[_lock lock];
_sharedVariable = objectNeedToPass;
[_lock unlockWithCondition:1];
- (void)thread2
while (true)
[_lock lockWithCondition:1];
id objectReceived = [_sharedVariable retain];
[_lock unlockWithCondition:0]
process(objectReceived );
[objectReceived release];
thread2 可能会看到 _sharedVariable
持有已释放的对象(并崩溃)
因为它可能会这样做
thread 1 create and autorelease object
thread 1 assign it to the shared variable
thread 1 release the object
object deallocated
thread 2 read the object
thread 2 retain the object - crash
要解决问题,你应该传递一个保留对象
id _sharedVariable; // ivar
NSConditionLock *_lock;
- (void)thread1
id objectNeedToPass = [[NSObject new] autorelease];
[_lock lock];
_sharedVariable = [objectNeedToPass retain];
[_lock unlockWithCondition:1];
- (void)thread2
while (true)
[_lock lockWithCondition:1];
id objectReceived = _sharedVariable;
[_lock unlockWithCondition:0]
process(objectReceived );
[objectReceived release];
但是,如果第二个线程未能释放对象并导致代码难以维护(保留/释放难以平衡),这可能会导致内存泄漏
【讨论】:
【参考方案2】:只要您遵循正常的 Cocoa 内存管理规则,就没有什么可担心的。只要您遵守规则,“将其提供给不同的线程”的每一种方式都可以正常工作。
几乎任何时候你“向不同的线程提供一些东西”,它都是异步的(除非你使用锁来做同步的跨线程执行或其他东西)。这意味着在该线程上的当前函数超出范围后,其他线程可能(并且可能)使用它。每当您存储需要超过当前执行的对象时,都需要保留它。如果您将它直接存储在实例变量或全局变量中,那么您有责任根据内存管理规则保留它。如果您将它存储在某种容器对象中,则该对象负责保留它。所以只要你遵守规则,几乎没有什么可担心的。
让我们考虑一种常见的方式,即人们在另一个线程上执行操作,-performSelector:onThread:withObject:waitUntilDone:
。如果waitUntilDone
为假,此函数将接收器、选择器和参数存储在某种对象中,以等待其他线程准备好执行它。因此,该函数必须负责在将接收者和对象放入此结构时保留它,并在结构销毁时释放它。确实如此 - 如果您阅读 the pre-ARC documentation for the method,它会说“此方法保留接收器和 arg 参数,直到执行选择器之后。”
所以基本上内存管理规则就足够了——如果将对象存储在实例变量中,则需要保留它。如果你将它传递给其他函数,那么他们的工作就是照顾它。
【讨论】:
【参考方案3】:不要。将拥有的引用传递给另一个线程。另一个线程将取得该对象的所有权,并在完成后释放它。
使用自动释放对象,您无法判断发送线程自动释放池何时会被耗尽,也无法确定它是否会在接收线程得到它之前被耗尽。
【讨论】:
定义拥有参考?为什么不在另一个线程中复制一份?以上是关于目标 C:线程之间的自动释放对象?的主要内容,如果未能解决你的问题,请参考以下文章
具有大量自动释放对象的线程如果是/否,在这种情况下是不是必须使用自动释放池,为啥?