Como converter NSMutableArray para NSArray?

381

Como converter NSMutableArray em NSArray em ?

Marcy
fonte
12
justNSArray *array = [mutableArray copy];
beryllium
11
NSArray *array = mutableArray;
vladof81
4
berílio: absolutamente certo. vladof81: Claro que não.
gnasher729

Respostas:

511
NSArray *array = [mutableArray copy];

Copyfaz cópias imutáveis. Isso é bastante útil porque a Apple pode fazer várias otimizações. Por exemplo, enviar copypara uma matriz imutável apenas retém o objeto e retorna self.

Se você não usar coleta de lixo ou ARC, lembre-se de que -copyretém o objeto.

Georg Schölly
fonte
2
essa resposta é ótima, mas como posso saber pelos documentos que copycria um NSArray normal e não um NSMutableArray?
Dan Rosenstark
22
@Yar: Você analisa a documentação da NSCopying Ela afirma: copyWithZone A cópia retornada é imutável se a consideração “imutável versus mutável” se aplicar ao objeto receptor; caso contrário, a natureza exata da cópia é determinada pela classe.
Georg Schölly
11
Muito arrumado - prefiro isso à solução da m5h. Conciso e mais eficiente.
David Snabel-Caunt
11
Concordo, David, atualizei minha resposta para apontar para essa resposta.
hallski
2
@ Brad: Isso significa apenas classes que têm implementações mutáveis ​​e imutáveis. Por exemplo, NSArray& NSMutableArray, NSString& NSMutableString. Mas não por exemplo, NSViewControllerque sempre contém estado mutável.
Georg Schölly
361

An NSMutableArrayé uma subclasse de, NSArrayportanto, você nem sempre precisará converter, mas se quiser garantir que a matriz não possa ser modificada, crie uma NSArraydas seguintes maneiras, dependendo se deseja ou não liberada automaticamente:

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

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

EDIT: A solução fornecida por Georg Schölly é uma maneira melhor de fazê-lo e muito mais limpa, especialmente agora que temos o ARC e nem precisamos chamar autorelease.

Hallski
fonte
7
Posso apenas fazer (NSArray *) myMutableArray?
Vojto
2
Sim, como NSMutableArray é uma subclasse do NSArray que é válida.
hallski
4
No entanto, a conversão para (NSArray *) ainda permite uma conversão para (NSMutable *). Não é esse o caso?
sharvey
11
@ Sharvey: Sim, está correto. Você receberá um aviso se não transmitir, mas atribuir uma superclasse a uma subclasse diretamente. Normalmente, você deseja retornar uma cópia imutável, porque essa é a única maneira de ter certeza de que sua matriz realmente não será modificada.
Georg Schölly
11
Anteriormente, isso é conhecido como "Upcasting" (NSArray *) myMutableArray e o inverso é chamado de "Downcasting"
Francisco Gutiérrez
113

Gosto das duas principais soluções:

NSArray *array = [NSArray arrayWithArray:mutableArray];

Ou

NSArray *array = [mutableArray copy];

A principal diferença que vejo neles é como eles se comportam quando mutableArray é nulo :

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

NSMutableArray *mutableArray = nil;
NSArray *array = [mutableArray copy];
// array == nil
Richard Venable
fonte
Isto é tão errado. Apenas verificado. [cópia mutableArray] também retorna uma matriz vazia.
durazno
@durazno Não está errado. Verifique seu teste. Se [cópia mutableArray] estiver retornando uma matriz vazia, então mutableArray deve ter sido uma matriz vazia. Minha resposta se aplica apenas quando mutableArray é nulo.
Richard Venable
Embora isso seja verdade, sinto que você está perdendo uma verdade mais fundamental em seu exemplo. Como você atribuiu mutableArray a zero, [mutableArray copy]pode ser simplificado para [nil copy]. No objetivo-c, qualquer mensagem enviada para zero será sempre nula. É importante lembrar a distinção entre "nada" e "uma matriz sem nada".
mkirk
@mkirk Não vamos nos distrair da pergunta em questão: "Como faço para converter o NSMutableArray em NSArray?" Existem 2 soluções principais, e a principal diferença entre elas é como elas se comportam quando a matriz mutável é nula.
Richard Venable
18

você tenta este código ---

NSMutableArray *myMutableArray = [myArray mutableCopy];

e

NSArray *myArray = [myMutableArray copy];
Jerry Thomsan
fonte
O que isso faz que os outros não fazem?
Ben leggiero
5

Objetivo-C

Abaixo está uma maneira de converter NSMutableArray em 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;

Rápido

No Swift 3.0, há um novo tipo de dados Array . Declare Array usando a letpalavra-chave, então ele se tornará NSArray E se declarar usando a varpalavra-chave, ele se tornará NSMutableArray .

Código de amostra:

let newArray = oldArray as Array
Sakir Sherasiya
fonte
A parte Swift não está totalmente certa. Veja aqui e aqui a diferença entre se mutável / imutável Arraye NSArray/ NSMutableArrays. Eles não são os mesmos.
Bruno Ely
3

No objetivo-c:

NSArray *myArray = [myMutableArray copy];

Em rápida:

 var arr = myMutableArray as NSArray
Vikram Biwal
fonte
2
NSArray *array = mutableArray;

este [mutableArray copy] antipadrão está em todo o código de amostra. Pare de fazer isso para matrizes mutáveis ​​descartáveis ​​que são transitórias e desalocadas no final do escopo atual.

Não há como o tempo de execução otimizar a cópia desnecessária de uma matriz mutável que está prestes a sair do escopo, decretada para 0 e desalocada para sempre.

Anton Tropashko
fonte
Atribuir um NSMutableArraya uma NSArrayvariável não o ocultará (que é a questão da OP). Expor esse valor (por exemplo, como um valor de retorno ou como uma propriedade) pode resultar em problemas de depuração muito difíceis se esse valor for alterado inesperadamente. Isso se aplica a mutações internas e externas, pois o valor mutável é compartilhado.
David Rönnqvist 10/10
Para alterar essa matriz, você teria que [NSMutableArray arrayWithArray: ... ou algo assim.
Anton Tropashko 11/11
Não necessariamente. Basta atribuí-lo a uma NSMutableArrayvariável. Como o objeto nunca deixou de ser uma matriz mutável, a chamada de métodos de mutação terá êxito e mutará os dados compartilhados. Se, por outro lado, a matriz mutável original foi copiada - alterando-a para uma matriz imutável - e depois atribuída a uma NSMutableArrayvariável e tivesse métodos de mutação chamados, essas chamadas falhariam com doesNotRespondToSelectorerros, porque o objeto que recebeu a chamada do método de mutação está em fato imutável e não responde a esses métodos.
David Rönnqvist
ok, isso pode ter algum mérito para os desenvolvedores que não acatam o aviso do compilador sobre assigments conversão tyope
Anton Tropashko
1

Se você estiver construindo um array via mutabilidade e quiser retornar uma versão imutável, basta retornar o array mutável como um "NSArray" por herança.

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

    return mutableArray;
}

Se você "confia" no chamador para tratar o objeto de retorno (tecnicamente ainda mutável) como um NSArray imutável, esta é uma opção mais barata que [mutableArray copy].

A Apple concorda:

Para determinar se ele pode alterar um objeto recebido, o destinatário de uma mensagem deve confiar no tipo formal do valor de retorno. Se receber, por exemplo, um objeto de matriz digitado como imutável, não deverá tentar mutá-lo . Não é uma prática de programação aceitável determinar se um objeto é mutável com base em sua associação à classe.

A prática acima é discutida em mais detalhes aqui:

Prática recomendada: retorne mutableArray.copy ou mutableArray se o tipo de retorno for NSArray

pkamb
fonte
0

Eu estava procurando a resposta no Swift 3 e esta pergunta foi mostrada como primeiro resultado na pesquisa. Eu me inspiro na resposta, então aqui está o código do Swift 3.

let array: [String] = nsMutableArrayObject.copy() as! [String]
Amr Angry
fonte