Як перетворити 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

Це NSMutableArrayпідклас, NSArrayтому вам не потрібно буде завжди конвертувати, але якщо ви хочете переконатися, що масив не може бути змінений, ви можете створити NSArrayбудь-який із цих способів залежно від того, хочете ви його автоматично випустити чи ні:

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

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

EDIT: Рішення, яке надає Георг Шьоллі, - це кращий спосіб зробити це і набагато чистіше, особливо зараз, коли у нас є ARC і навіть не потрібно викликати автовипуск.


7
Чи можу я просто зробити (NSArray *) myMutableArray?
Vojto

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

@durazno Це не так. Перевірте тест ще раз. Якщо [mutableArray copy] повертає порожній масив, то mutableArray повинен бути порожнім масивом. Моя відповідь застосовується лише тоді, коли mutableArray є нульовим.
Річард Венебл

Хоча це правда, я відчуваю, що у вашому прикладі ви пропускаєте більш фундаментальну істину. Оскільки ви присвоїли mutableArray нульове значення, [mutableArray copy]його можна спростити [nil copy]. У предметі-c будь - яке повідомлення, надіслане до нуля, завжди буде нульовим. Важливо пам’ятати про відмінність "нічого" від "масиву, в якому нічого немає".
mkirk

@mkirk Давайте не відволікаємось від питання: "Як перетворити NSMutableArray в NSArray?" Існує 2 первинних рішення, і первинна відмінність між ними полягає в тому, як вони поводяться, коли змінний масив є нульовим.
Річард Венебл


5

Ціль-С

Нижче наведено спосіб перетворення 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. Вони не однакові.
Бруно Елі

3

В Objective-c:

NSArray *myArray = [myMutableArray copy];

Швидко:

 var arr = myMutableArray as NSArray

2
NSArray *array = mutableArray;

Цей [mutableArray copy]антипатерн є повним зразком коду. Перестаньте це робити для викидних змінних масивів, які є тимчасовими, і отримайте дислокацію в кінці поточної області.

Ні в якому разі час виконання не може оптимізувати марнотратне копіювання мутаційного масиву, який збирається вийти за межі, відключений до 0 і назавжди розподілений.


Присвоєння NSMutableArrayдо NSArrayзмінної не таємному його (що питання в OP становить близько). Виявлення такого значення (наприклад, як повернене значення або як властивість) може призвести до дуже важких проблем налагодження, якщо це значення було несподівано змінено. Це стосується як внутрішніх, так і зовнішніх мутацій, оскільки значення, що змінюється, є спільним.
David Rönnqvist

Щоб вимкнути цей масив, вам доведеться [NSMutableArray arrayWithArray: ... або щось подібне.
Антон Тропашко

Не обов'язково. Досить просто призначити його NSMutableArrayзмінної. Оскільки об'єкт ніколи не переставав бути мутаційним масивом, виклик методів, що мутують його, буде успішним та мутуватиме спільні дані. Якщо, з іншого боку, було скопійовано оригінальний змінний масив - змінивши його на незмінний масив - і потім призначивши NSMutableArrayзмінну та застосувавши до нього мутаційні методи, ці виклики не матимуть doesNotRespondToSelectorпомилок, оскільки об'єкт, який отримав виклик мутаційного методу, знаходиться в факт незмінний і не відповідає на ці методи.
David Rönnqvist

ОК, це може мати певну заслугу для розробників, які не прислухаються до попередження компілятора про призначення конверсії tyope
Антон Тропашко,

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

я шукав відповідь у швидкому 3, і це запитання було показано як перший результат у пошуку, і я отримав натхненну відповідь від нього, ось ось код swift 3

let array: [String] = nsMutableArrayObject.copy() as! [String]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.