Search code examples
objective-cautomatic-ref-countingmanual-retain-release

How can I safely go between ARC and MRC methods without unnecessary retain/release calls?


I have an ARC class with the following code:

[object doStuffWithObject:otherObject];

object's -doStuffWithObject: method is compiled with ARC, and is this:

- (void)doStuffWithObject:(id)otherObject
{
    DoStuffHelper(object, otherObject);
}

DoStuffHelper, a C function, is not compiled with ARC (for performance reasons). In DoStuffHelper, do I need to call -retain at the start for object and otherObject and -release for them at the end?


Solution

  • See Avoid Causing Deallocation of Objects You’re Using in Advance Memory Management: Practical Memory Management, in which they point out that "received objects should typically remain valid throughout the scope of the calling method", i.e. retain and release are not necessary, except with the following caveat:

    There are occasional exceptions to this rule, primarily falling into one of two categories.

    1. When an object is removed from one of the fundamental collection classes.

      heisenObject = [array objectAtIndex:n];
      [array removeObjectAtIndex:n];
      // heisenObject could now be invalid.
      

      When an object is removed from one of the fundamental collection classes, it is sent a release (rather than autorelease) message. If the collection was the only owner of the removed object, the removed object (heisenObject in the example) is then immediately deallocated.

    2. When a “parent object” is deallocated.

      id parent = <#create a parent object#>;
       // ...
       heisenObject = [parent child] ;
       [parent release]; // Or, for example: self.parent = nil;
       // heisenObject could now be invalid.
      

      In some situations you retrieve an object from another object, and then directly or indirectly release the parent object. If releasing the parent causes it to be deallocated, and the parent was the only owner of the child, then the child (heisenObject in the example) will be deallocated at the same time (assuming that it is sent a release rather than an autorelease message in the parent’s dealloc method).

    To protect against these situations, you retain heisenObject upon receiving it and you release it when you have finished with it. For example:

    heisenObject = [[array objectAtIndex:n] retain];
    [array removeObjectAtIndex:n];
    // Use heisenObject...
    [heisenObject release];
    

    I'd be surprised if your helper function fell into one of those categories. You'll probably be fine without the retain and release, but I only mention it for the sake of full disclosure.

    Obviously, you might need your own retain and release if your functions need it for other reasons (e.g. if task is asynchronous or if you are otherwise doing something unusual where the object must outlive the scope of the calling method), but I suspect you would have mentioned it if you were doing one of those things.

    Also, if your utility function was creating and returning an object, then that would be a different matter (e.g. you'd generally autorelease objects that you were returning).