Возврат объекта NSMutableArray из метода с возвращаемым значением NSArray

При возврате NSArray (или NSDictionary и т.д.) из метода, который строит массив "на лету" с помощью NSMutableArray, что является стандартным способом для этого и избегать случайных утечек памяти при использовании ARC

Например, скажем, у нас был класс со списком имен, и мы хотели вручную отфильтровать и захватить все имена, которые начинались с данной буквы:

- (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
}

Когда дело доходит до возврата значения, я могу представить четыре возможных способа сделать это:

  • Верните NSMutableArray напрямую (как указано выше)

    return returnValue;
    
  • Верните a copy

    return [returnValue copy];
    
  • Возврат с помощью NSArray arrayWithArray:

    return [NSArray arrayWithArray:returnValue];
    
  • Создайте NSArray, вручную установите NSMutableArray в nil:

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

Когда программа использует ARC, существует ли какая-либо реальная разница между этими четырьмя методами или она просто сводится к личным предпочтениям?

Кроме того, помимо возможных утечек памяти, есть ли какие-либо другие последствия при использовании одного метода над другим?

Заметьте, если это дубликат, дайте мне знать, и я отвечу на вопрос. Я попытался выполнить поиск, но с трудом пытался свести проблему до нескольких поисковых запросов.

Ответ 1

Все четыре из ваших вариантов в порядке с поддержкой ARC (т.е. ни одно из предложенных решений не приведет к утечке памяти).

Однако 4 решения, которые вы наметили, делают несколько разные вещи. Номер 1 вернет NSMutableArray, который, вероятно, не вызовет проблем, потому что NSMutableArray будет отвечать на все те же сообщения, что и NSArray (но возвращенный объект будет изменен, что вам может и не понадобиться).

Существует тонкая разница между опцией 2 и вариантами 3 и 4 (которые идентичны при ARC). Если returnValue - nil, опция 2 вернет nil, но опции 3 и 4 вернут пустой NSArray. (Любое поведение может быть желательно, вы должны решить, как вы хотите, чтобы этот метод вел себя). Кроме того, -copy скорее всего работает быстрее, чем +arrayWithArray.

Я бы выбрал вариант 2.

Ответ 2

Прежде всего: ни один из подходов, которые вы обсуждаете, не приведет к утечке памяти под ARC. Кроме того, подход номер четыре не требует, чтобы вы установили returnValue в nil - компилятор достаточно умен, чтобы самостоятельно позаботиться о returnValue. Это делает подход номер четыре точно таким же, как номер три.

Кроме того, вызов copy on returnValue аналогичен созданию нового массива из его содержимого, поэтому второй подход совпадает с вторым.

Это оставляет нам два подхода - первый, второй и третий/четвертый. Основные различия между ними в том, что пользователь сможет сделать с массивом, который он получает от вашего метода. Первый подход позволяет пользователю изменять массив; последние три - нет. Решение о том, какое поведение вы предпочитаете, зависит от вас.

Ответ 3

С одной стороны, в данном примере нет утечек, заключается в том, что все знают, что NSArray может быть изменчивым. Если вы получите его в результате вызова метода, зависит от вас, скопируете ли вы его или используете как есть, будучи готовым к его изменению в любое время. Вот почему неизменяемые свойства часто объявляются как скопированные, а не сильные. Присвоение изменяемого значения этому свойству будет выполнять реальную копию, а присвоение неизменяемого значения будет дешевым.

tl; dr: просто верните returnValue, это нормально.