Как я могу преобразовать NSMutableArray в NSArray?


Ответы:


511
NSArray *array = [mutableArray copy];

Copyделает неизменные копии. Это очень полезно, потому что Apple может делать различные оптимизации. Например, отправка copyв неизменяемый массив только сохраняет объект и возвращает self.

Если вы не используете сборщик мусора или ARC, помните, что -copyсохраняет объект.


2
этот ответ copyхорош , но как я могу узнать из документов, которые создают обычный NSArray, а не NSMutableArray?
Дэн Розенстарк

22
@Yar: Вы просматриваете документацию NSCopying. Там говорится: copyWithZone. Возвращаемая копия является неизменной, если к получающему объекту применяется соображение «неизменяемый и изменяемый»; в противном случае точный характер копии определяется классом.
Георг Шолли

1
Очень аккуратно - я предпочитаю это решению m5h. И кратко и более эффективно.
Дэвид Снабель-Каунт

1
Я согласен, Дэвид, обновил мой ответ, чтобы указать на этот ответ.
hallski

2
@Brad: Это просто означает, что классы имеют как изменяемые, так и неизменные реализации. Например, NSArray& NSMutableArray, NSString& NSMutableString. Но не например, NSViewControllerкоторый всегда содержит изменяемое состояние.
Георг Шолли

361

An NSMutableArrayявляется подклассом, NSArrayтак что вам не всегда нужно конвертировать, но если вы хотите убедиться, что массив не может быть изменен, вы можете создать NSArrayлюбой из этих способов в зависимости от того, хотите ли вы, чтобы он был автоматически освобожден или нет:

/* Not autoreleased */
NSArray *array = [[NSArray alloc] initWithArray:mutableArray];

/* Autoreleased array */
NSArray *array = [NSArray arrayWithArray:mutableArray];

РЕДАКТИРОВАТЬ: Решение, предоставленное Георгом Schölly, является лучшим способом сделать это и намного чище, особенно теперь, когда у нас есть ARC и даже не нужно вызывать autorelease.


7
Могу ли я просто сделать (NSArray *) myMutableArray?
Войто

2
Да, поскольку NSMutableArray является действующим подклассом NSArray.
hallski

4
Однако приведение к (NSArray *) все еще позволяет приведение обратно к (NSMutable *). Разве это не так?
Шарви

1
@sharvey: Да, это правильно. Вы получите предупреждение, если не будете разыгрывать, а назначаете суперкласс непосредственно подклассу. Обычно вы хотите вернуть неизменяемую копию, потому что это единственный способ убедиться, что ваш массив действительно не будет изменен.
Георг Шолли

1
Ранее это называлось «Upcasting» (NSArray *) myMutableArray, а обратное называется «Downcasting»
Франциско Гутьеррес,

113

Мне нравятся оба основных решения:

NSArray *array = [NSArray arrayWithArray:mutableArray];

Или

NSArray *array = [mutableArray copy];

Основное различие я вижу в них , как они ведут себя , когда mutableArray равна нулю :

NSMutableArray *mutableArray = nil;
NSArray *array = [NSArray arrayWithArray:mutableArray];
// array == @[] (empty array)

NSMutableArray *mutableArray = nil;
NSArray *array = [mutableArray copy];
// array == nil

Это так неправильно. Только что проверил. [mutableArray copy] также возвращает пустой массив.
Дуразно

@durazno Это не так. Дважды проверьте свой тест. Если [mutableArray copy] возвращает пустой массив, тогда mutableArray должен был быть пустым массивом. Мой ответ применяется только тогда, когда mutableArray равен нулю.
Ричард Венейбл

Хотя это правда, я чувствую, что вы упускаете более фундаментальную истину в своем примере. Поскольку вы присвоили mutableArray значение nil, [mutableArray copy]его можно упростить до [nil copy]. В target-c любое сообщение, отправленное на ноль, всегда будет ноль. Важно помнить различие между «ничем» и «массивом, в котором ничего нет».
Mkirk

@mkirk Давайте не будем отвлекаться от вопроса: «Как мне преобразовать NSMutableArray в NSArray?» Существует 2 основных решения, и основное различие между ними заключается в том, как они ведут себя, когда изменяемый массив равен нулю.
Ричард Венейбл


5

Objective-C

Ниже приведен способ преобразования NSMutableArray в NSArray:

//oldArray is having NSMutableArray data-type.
//Using Init with Array method.
NSArray *newArray1 = [[NSArray alloc]initWithArray:oldArray];

//Make copy of array
NSArray *newArray2 = [oldArray copy];

//Make mutablecopy of array
NSArray *newArray3 = [oldArray mutableCopy];

//Directly stored NSMutableArray to NSArray.
NSArray *newArray4 = oldArray;

стриж

В Swift 3.0 появился новый тип данных Array . Объявите массив с помощью letключевого слова, тогда он станет NSArray. А если объявите с помощью varключевого слова, то он станет NSMutableArray .

Образец кода:

let newArray = oldArray as Array

Часть Swift не совсем верна. Смотрите здесь и здесь для различия между изменяемыми / неизменяемыми Arrays и NSArray/ NSMutableArrays. Они не одинаковы.
Бруно Эли


2
NSArray *array = mutableArray;

Этот [mutableArray copy]антипаттерн есть во всем примере кода. Прекратите делать это для одноразовых изменяемых массивов, которые являются временными и освобождаются в конце текущей области.

Нет способа, которым среда выполнения могла бы оптимизировать бесполезное копирование изменяемого массива, который вот-вот выйдет из области видимости, уменьшится до 0 и будет освобожден навсегда.


Присвоение NSMutableArrayк NSArrayпеременной не тайному его (что вопрос в OP составляет около). Предоставление такого значения (например, в качестве возвращаемого значения или в качестве свойства) может привести к очень сложным проблемам отладки, если это значение было неожиданно изменено. Это относится как к внутренним, так и к внешним мутациям, поскольку изменяемое значение является общим.
Дэвид Ренквист

Чтобы изменить этот массив, вам нужно [NSMutableArray arrayWithArray: ... или что-то в этом роде.
Антон Тропашко

Не обязательно. Достаточно просто присвоить его NSMutableArrayпеременной. Поскольку объект никогда не переставал быть изменяемым массивом, вызов методов с мутацией для него завершится успешно и приведет к изменению общих данных. Если, с другой стороны, исходный изменяемый массив был скопирован, изменив его на неизменяемый массив, а затем назначен NSMutableArrayпеременной и вызвав методы мутации, эти вызовы завершатся ошибкой, doesNotRespondToSelectorпоскольку объект, получивший вызов метода мутации, находится в факт неизменен и не отвечает на эти методы.
Дэвид Ренквист,

хорошо, это может быть полезно разработчикам, которые не обращают внимания на предупреждение компилятора о настройках преобразования тиопов
Антон Тропашко

1

Если вы создаете массив с помощью изменчивости, а затем хотите вернуть неизменяемую версию, вы можете просто вернуть изменяемый массив как «NSArray» через наследование.

- (NSArray *)arrayOfStrings {
    NSMutableArray *mutableArray = [NSMutableArray array];
    mutableArray[0] = @"foo";
    mutableArray[1] = @"bar";

    return mutableArray;
}

Если вы «доверяете» вызывающей стороне для обработки (технически все еще изменяемого) возвращаемого объекта как неизменяемого NSArray, это более дешевый вариант, чем [mutableArray copy].

Apple соглашается:

Чтобы определить, может ли он изменить полученный объект, получатель сообщения должен полагаться на формальный тип возвращаемого значения. Если он получает, например, объект массива, типизированный как неизменяемый, он не должен пытаться изменить его . Неприемлемой практикой программирования является определение того, является ли объект изменчивым, основываясь на его принадлежности к классу.

Вышеуказанная практика обсуждается более подробно здесь:

Рекомендация. Возвращайте mutableArray.copy или mutableArray, если тип возвращаемого значения - NSArray.


0

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

let array: [String] = nsMutableArrayObject.copy() as! [String]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.