目标 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:线程之间的自动释放对象?的主要内容,如果未能解决你的问题,请参考以下文章

具有大量自动释放对象的线程如果是/否,在这种情况下是不是必须使用自动释放池,为啥?

使用 NSOperationQueue 在单独的线程上创建自动释放的对象

目标 C:保留/释放内联分配的对象

C# 线程运行完之后自己会不会释放掉

在 posix 线程上创建自动释放池

Objective C - 内存管理和自动释放???