1

If I have an autoreleased object and I need to provide it to a different thread, what is the best way to do so?

Let's say I have an object that is autoreleased in thread 0. I tell thread 1 about this object and it retains it because it needs it. Later then it's done, it releases it. No problem. When thread 0 runs again and empties its autorelease pool, it sees the retain count is 1 and because it's an autoreleased object it deallocs. Everything is fine, therefore threads don't matter. Right?

By the way this was originally an interview question. The interviewer insisted that an autoreleased object cannot be given to another thread. He seemed almost angry about it. More and more in tech interviews, I encounter ppl who believe they know everything.

Friendly
  • 233
  • 1
  • 7

3 Answers3

0

You should not pass autoreleased object directly to other thread.

in this code

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 may see _sharedVariable hold a released object (and crash)

because it may do this

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

to solve the problem, you should pass a retained object

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];
    }
}

however, this may cause memory leak if second thread failed to release the object and make code hard to maintain (retain/release are hard to balance)

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
0

There is nothing to worry about at all as long as you are following the normal Cocoa memory management rules. Every single way of "providing it to a different thread" will work fine as long as you are following the rules.

Pretty much any time you "provide something to a different thread", it is asynchronous (unless you are using locks to do synchronous cross-thread execution or something). Which means that the other thread may (and will likely) use it after the current function on this thread has gone out of scope. Any time you store an object that needs to outlive the current execution, it needs to be retained. If you are storing it in an instance variable or global variable directly, then you are responsible for retaining it, according to the memory management rules. If you are storing it in some kind of container object, then that object is responsible for retaining it. So pretty much if you follow the rules, there is nothing to worry about.

Let's consider a common way that people execute things on another thread, with -performSelector:onThread:withObject:waitUntilDone:. If waitUntilDone is false, this function stores the receiver, selector, and argument in some kind of object to wait until the other thread is ready to execute it. Therefore, this function must be responsible for retaining the receiver and object when it places it into this structure, and releasing it when the structure is destroyed. And indeed it does -- if you read the pre-ARC documentation for the method, it says "This method retains the receiver and the arg parameter until after the selector is performed."

So basically the memory management rules are sufficient -- if you store the object in an instance variable, you need to retain it. If you pass it to some other function, then it's their job to take care of it.

newacct
  • 119,665
  • 29
  • 163
  • 224
-1

Don't. Pass an owning reference to the other thread. The other thread will take ownership of the object and release it when done with it.

With autoreleased objects, you can't tell when the sending threads autorelease pool will be drained, and can't be sure if it will be drained before the receiving thread gets it.

Duncan C
  • 128,072
  • 22
  • 173
  • 272