3

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?

rmaddy
  • 298,130
  • 40
  • 468
  • 517
meisel
  • 1,783
  • 2
  • 17
  • 30
  • 4
    Please explain how ARC is a performance issue. All ARC does is add appropriate calls to `retain` and `release` for you where you would be adding them yourself with MRC. – rmaddy Sep 23 '17 at 02:28
  • @meisel ARC is actually faster than manual retain/release. It avoids the autorelease pool and retain/release calls in places that correct MRR code cannot. – bbum Oct 03 '17 at 16:22

4 Answers4

2

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).

Rob
  • 371,891
  • 67
  • 713
  • 902
1

In the vast majority of cases you won't find ARC slower than traditional retain/release. Have you used Instruments to verify the problem?

Having said that, you do not need to retain/release the object in DoStuffHelper() because the retainCount will already be >= 1 on entry. If store the objects in a static or global then you will need to retain them.

EricS
  • 9,408
  • 2
  • 34
  • 34
  • Why would I need to retain the object if it's in a static or global? That doesn't sound like one of the 3 situations that Rob mentioned in his answer. – meisel Sep 25 '17 at 18:25
  • When you store the object in an NSArray or NSDictionary, it is automatically retained. When you store the object in a static or global, it is not automatically retained if ARC is off. Think of it this way: The retainCount is 1 when the routine is called. You store the object in a global and it remains 1 because ARC is off. You return to the caller who no longer needs the object and releases it, which decrements the retainCount to zero and releases the memory. Now your global/static variable points to deallocated memory and your app will likely crash the next time you access it. – EricS Sep 25 '17 at 22:39
  • Oh, you're referring specifically to globals within the non-ARC file. That makes sense. – meisel Sep 25 '17 at 22:48
1

No. DoStuffHelper is not an object that asserts ownership of object or otherObject. It's just a utility function that operates on them directly. In effect, it's part of your ARC class, which is already performing complete memory management on these objects.

matt
  • 447,615
  • 74
  • 748
  • 977
1

DoStuffHelper, a C function, is not compiled with ARC (for performance reasons).

Do you have any performance measurements that show ARC to be slower?

In general, ARC'd executables generated with the optimizer turned on will be faster than the same code with ARC disabled (and optimized by the compiler).

This is because the compiler (and the runtime) can reason about the references to the point of being able to avoid calls to retain and release (and, worse, autorelease) wherein said calls would be mandatory in correct MRR code.

If you have a code pattern where that is not the case, I'd like to capture said pattern and file a bug.

(This is, admittedly, a meta-answer. It questions whether the need for such an ARC-MRR interface is even necessary. Having maintained code bases that are partially ARC and partially MRR, such mixes are rife with fragility.)

bbum
  • 160,467
  • 23
  • 266
  • 355
  • @meisel. Cool. If you could share an example, could you please file a bug via bugreporter.apple.com. Or, if you want, feel free to send me the code (bbum@) and I can file the bug directly. Thanks! – bbum Oct 04 '17 at 18:11