No Cocoa, se eu quiser fazer um loop através de um NSMutableArray e remover vários objetos que atendem a um determinado critério, qual é a melhor maneira de fazer isso sem reiniciar o loop toda vez que removo um objeto?
Obrigado,
Edit: Apenas para esclarecer - eu estava procurando a melhor maneira, por exemplo, algo mais elegante do que atualizar manualmente o índice em que estou. Por exemplo, em C ++ eu posso fazer;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
objective-c
cocoa
Andrew Grant
fonte
fonte
Respostas:
Para maior clareza, gosto de fazer um loop inicial onde coleciono os itens a serem excluídos. Então eu os apago. Aqui está um exemplo usando a sintaxe do Objective-C 2.0:
Não há dúvidas sobre se os índices estão sendo atualizados corretamente ou outros pequenos detalhes da contabilidade.
Editado para adicionar:
Observou-se em outras respostas que a formulação inversa deve ser mais rápida. ou seja, se você percorrer a matriz e compor uma nova matriz de objetos a serem mantidos, em vez de objetos a serem descartados. Isso pode ser verdade (embora o que dizer da memória e do custo de processamento de alocar uma nova matriz e descartar a antiga?), Mas mesmo que seja mais rápido, pode não ser tão importante quanto seria para uma implementação ingênua, porque o NSArrays não se comporte como matrizes "normais". Eles falam a conversa, mas andam uma caminhada diferente. Veja uma boa análise aqui:
A formulação inversa pode ser mais rápida, mas nunca precisei me importar se é, porque a formulação acima sempre foi rápida o suficiente para minhas necessidades.
Para mim, a mensagem para levar para casa é usar qualquer formulação que seja mais clara para você. Otimize apenas se necessário. Pessoalmente, acho a formulação acima mais clara, e é por isso que a uso. Mas se a formulação inversa for mais clara para você, faça isso.
fonte
Mais uma variação. Assim, você obtém legibilidade e bom desempenho:
fonte
removeObjectsAtIndexes
é o pior método para remover os objetos, você concorda com isso? Estou perguntando isso porque sua resposta é muito antiga agora. Ainda assim, é bom escolher o melhor?enumerateObjectsUsingBlock:
obteria o incremento do índice gratuitamente.Este é um problema muito simples. Você apenas itera para trás:
Este é um padrão muito comum.
fonte
Algumas das outras respostas teriam um desempenho ruim em matrizes muito grandes, porque os métodos gostam
removeObject:
eremoveObjectsInArray:
envolvem fazer uma pesquisa linear do receptor, o que é um desperdício, porque você já sabe onde está o objeto. Além disso, qualquer chamada pararemoveObjectAtIndex:
terá que copiar valores do índice para o final da matriz em um slot por vez.Mais eficiente seria o seguinte:
Como definimos a capacidade de
itemsToKeep
, não perdemos tempo copiando valores durante um redimensionamento. Como não modificamos a matriz no local, estamos livres para usar a Enumeração rápida. UsarsetArray:
para substituir o conteúdo dearray
comitemsToKeep
será eficiente. Dependendo do seu código, você pode até substituir a última linha por:Portanto, não há nem a necessidade de copiar valores, apenas trocar um ponteiro.
fonte
Você pode usar o NSpredicate para remover itens da sua matriz mutável. Isso não requer para loops.
Por exemplo, se você tiver um NSMutableArray de nomes, poderá criar um predicado como este:
A linha a seguir apresentará uma matriz que contém apenas nomes começando com b.
Se você tiver problemas para criar os predicados necessários, use este link para desenvolvedor da apple .
fonte
Eu fiz um teste de desempenho usando 4 métodos diferentes. Cada teste repetiu todos os elementos em uma matriz de 100.000 elementos e removeu cada quinto item. Os resultados não variaram muito com / sem otimização. Isso foi feito em um iPad 4:
(1)
removeObjectAtIndex:
- 271 ms(2)
removeObjectsAtIndexes:
- 1010 ms (porque a criação do conjunto de índices leva ~ 700 ms; caso contrário, é basicamente o mesmo que chamar removeObjectAtIndex: para cada item)(3)
removeObjects:
- 326 ms(4) faça uma nova matriz com objetos que passam no teste - 17 ms
Portanto, criar uma nova matriz é de longe o mais rápido. Os outros métodos são todos comparáveis, exceto que o uso de removeObjectsAtIndexes: será pior com mais itens a serem removidos, devido ao tempo necessário para criar o conjunto de índices.
fonte
Use o loop com contagem decrescente sobre os índices:
ou faça uma cópia com os objetos que você deseja manter.
Em particular, não use um
for (id object in array)
loop ouNSEnumerator
.fonte
Para iOS 4+ ou OS X 10.6+, a Apple adicionou uma
passingTest
série de APIs emNSMutableArray
, como– indexesOfObjectsPassingTest:
. Uma solução com essa API seria:fonte
Atualmente, você pode usar a enumeração baseada em bloco invertida. Um código de exemplo simples:
Resultado:
outra opção com apenas uma linha de código:
fonte
De uma maneira mais declarativa, dependendo dos critérios correspondentes aos itens a serem removidos, você pode usar:
@ Nathan deve ser muito eficiente
fonte
Aqui está a maneira fácil e limpa. Eu gosto de duplicar minha matriz diretamente na chamada de enumeração rápida:
Dessa forma, você enumera por meio de uma cópia da matriz da qual está sendo excluído, ambos mantendo os mesmos objetos. Um NSArray contém ponteiros de objeto apenas, portanto, este é um ótimo desempenho de memória / desempenho.
fonte
for (LineItem *item in self.lineItems.copy)
Adicione os objetos que você deseja remover a uma segunda matriz e, após o loop, use -removeObjectsInArray :.
fonte
Isso deve servir:
espero que isto ajude...
fonte
Por que você não adiciona os objetos a serem removidos para outro NSMutableArray. Quando você terminar de iterar, poderá remover os objetos que você coletou.
fonte
Que tal trocar os elementos que você deseja excluir pelo elemento "n", "n-1" e assim por diante?
Quando terminar, redimensione a matriz para 'tamanho anterior - número de trocas'
fonte
Se todos os objetos em sua matriz forem exclusivos ou você desejar remover todas as ocorrências de um objeto quando encontrado, você poderá enumerar rapidamente em uma cópia da matriz e usar [NSMutableArray removeObject:] para remover o objeto do original.
fonte
+arrayWithArray
estiver sendo executado?A resposta do benzado acima é o que você deve fazer para obter a pré-forma. Em um dos meus aplicativos, o removeObjectsInArray levou um tempo de execução de 1 minuto, apenas a adição a uma nova matriz levou 0,023 segundos.
fonte
Defino uma categoria que me permite filtrar usando um bloco, assim:
que pode ser usado assim:
fonte
Uma implementação melhor seria usar o método de categoria abaixo no NSMutableArray.
O bloco de predicado pode ser implementado para processar em cada objeto na matriz. Se o predicado retornar true, o objeto será removido.
Um exemplo para uma matriz de datas para remover todas as datas anteriores:
fonte
Iterar para trás era o meu favorito por anos, mas por um longo tempo nunca encontrei o caso em que o objeto 'mais profundo' (contagem mais alta) foi removido primeiro. Momentaneamente antes de o ponteiro passar para o próximo índice, não há nada e ele trava.
O caminho de Benzado é o mais próximo do que faço agora, mas eu nunca percebi que haveria a reorganização da pilha após cada remoção.
no Xcode 6 isso funciona
fonte