11

When returning an NSArray (or NSDictionary, etc.) from a method that builds the array on the fly using an NSMutableArray, what is the standard way to do this and avoid random memory leaks when using ARC?

For example, let's say we had some class with a list of names, and we wanted to manually filter and grab all of the names that started with a given letter:

- (NSArray *)getNamesByFirstLetter:(NSString *)firstLetter 
{
    NSMutableArray *returnValue = [[NSMutableArray alloc] init];
    for(id item in self.names)
    {
        if([item hasPrefix:firstLetter])
        {
            [returnValue addObject:item];
        }
    }        
    return returnValue; // just return the above array
}

When it comes to returning the value, I can think of four possible ways to do it:

  1. Return the NSMutableArray directly (as above)

    return returnValue;
    
  2. Return a copy

    return [returnValue copy];
    
  3. Return using NSArray arrayWithArray:

    return [NSArray arrayWithArray:returnValue];
    
  4. Create an NSArray, manually set the NSMutableArray to nil:

    NSArray *temp = [NSArray arrayWithArray:returnValue]; // could use [returnValue copy] here too
    returnValue = nil;
    return temp;
    

When a program is using ARC, is there any real difference between these four methods, or does it just come down to personal preference?

Also, besides possible memory leaks, are there any other implications when using one method over another?

Note, if this is a duplicate, let me know, and I'll take the question down. I tried searching, but had a hard time trying to condense the issue down to a few search terms.

valverij
  • 4,871
  • 1
  • 22
  • 35
  • 3
    Without going into detail, none of these will result in a memory leak with ARC enabled. I must mention though that 4 and 3 are equivalent in ARC environment – Jack Feb 11 '14 at 16:27

3 Answers3

9

All four of your options are fine with ARC enabled (i.e. none of your proposed solutions will cause a memory leak).

However, the 4 solutions you outline do slightly different things. Number 1 will return an NSMutableArray, which likely won't cause problems because NSMutableArray will respond to all the same messages as NSArray (but the returned object will be mutable, which you might not want).

There is a subtle difference between option 2 and options 3 & 4 (which are identical under ARC). If returnValue is nil, option 2 will return nil, but options 3 & 4 will return an empty NSArray. (Either behavior might be desirable; you should decide how you want this method to behave). Also, -copy is likely a faster operation than +arrayWithArray.

I would go with option 2.

valverij
  • 4,871
  • 1
  • 22
  • 35
erik
  • 106
  • 2
  • Makes sense. I'm just so used to working with a garbage collected environment, I guess I'm just a bit paranoid. Also, further research led me to [this question](http://stackoverflow.com/questions/14849570) and [this question](http://stackoverflow.com/questions/1391914), which say that `arrayWithArray` is explicitly set to autorelease, while the `copy` is not necessarily. Is it safe to assume that this difference only matters in programs not using ARC? – valverij Feb 11 '14 at 16:57
  • @valverij In garbage collected environments it is also not necessary (or even desirable) to set your variables to `null`. Additionally, ARC hides the difference between autoreleasing and plain releasing from the programmers, so it is safe to ignore this particular difference in your case when compiling with ARC. – Sergey Kalinichenko Feb 11 '14 at 17:06
  • @dasblinkenlight, I know garbage collected environments don't require you to explicitly set variables to `null`, that's why I was worried about missing something with Objective-C. Thanks, though, it's good to know that ARC has all of that covered. – valverij Feb 11 '14 at 17:09
3

First thing first: none of the approaches that you discuss would result in a memory leak under ARC. Moreover, approach number four does not require you to set returnValue to nil - the compiler is smart enough to take care of the returnValue on its own. This makes the approach number four to be exactly the same as the number three.

Additionally, calling a copy on returnValue is the same as creating a new array from its content, so the second approach is the same as the last two.

This leaves us with two approaches - the first one, and the second/third/forth. The major differences between them in what the user would be able to do with the array that he gets back from your method. The first approach let the user modify the array; the last three do not. Deciding on which behavior you would prefer is up to you.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

One thing beside that there is no leaks in the given example, is that everyone knows that NSArray may be mutable. If you get it as result of method call, it is up to you whether you copy it or use as-is, being ready for it to change anytime. That's why immutable properties are often declared as copy instead of strong. Assigning mutable value to that property will do real copy, and assigning immutable value is cheap.

tl;dr: just return returnValue, it is OK.

user3125367
  • 2,920
  • 1
  • 17
  • 17