Além das diferenças óbvias:
- Use
enumerateObjectsUsingBlock
quando você precisar do índice e do objeto Não use(eu estava errado sobre isso, veja a resposta do bbum)enumerateObjectsUsingBlock
quando precisar modificar variáveis locais
enumerateObjectsUsingBlock
Geralmente é considerado melhor ou pior quando for (id obj in myArray)
também funcionaria? Quais são as vantagens / desvantagens (por exemplo, é mais ou menos desempenho)?
objective-c
multithreading
ios4
objective-c-blocks
Paul Wheeler
fonte
fonte
Respostas:
Por fim, use o padrão que você deseja usar e seja mais natural no contexto.
Embora
for(... in ...)
seja bastante conveniente e sintaticamente breve,enumerateObjectsUsingBlock:
possui vários recursos que podem ou não ser interessantes:enumerateObjectsUsingBlock:
será tão rápido ou mais rápido que a enumeração rápida (for(... in ...)
usa oNSFastEnumeration
suporte para implementar a enumeração). A enumeração rápida requer tradução de uma representação interna para a representação para enumeração rápida. Há sobrecarga nele. A enumeração baseada em bloco permite que a classe de coleção enumere o conteúdo tão rapidamente quanto a travessia mais rápida do formato de armazenamento nativo. Provavelmente irrelevante para matrizes, mas pode ser uma enorme diferença para dicionários."Não use enumerateObjectsUsingBlock quando precisar modificar variáveis locais" - não é verdade; você pode declarar seus locais como
__block
e eles serão graváveis no bloco.enumerateObjectsWithOptions:usingBlock:
suporta enumeração simultânea ou reversa.com dicionários, a enumeração baseada em bloco é a única maneira de recuperar a chave e o valor simultaneamente.
Pessoalmente, uso com
enumerateObjectsUsingBlock:
mais frequência do quefor (... in ...)
, mas - novamente - escolha pessoal.fonte
enumerateObjectsUsingBlock
ainda é significativamente mais lento que a enumeração rápida para matrizes e conjuntos. Eu quero saber porque? iosdevelopertips.com/objective-c/...enumerateObjectsUsingBlock
envolvem cada chamada do bloco com um pool de liberação automática (pelo menos no OS X 10.10). Isso explica a diferença de desempenho em comparação àfor in
qual não faz isso.Para enumeração simples, basta usar a enumeração rápida (ou seja, um
for…in…
loop) é a opção mais idiomática. O método de bloco pode ser um pouco mais rápido, mas isso não importa muito na maioria dos casos - poucos programas são vinculados à CPU e, mesmo assim, é raro que o próprio loop em vez da computação interna seja um gargalo.Um loop simples também lê com mais clareza. Aqui está o boilerplate das duas versões:
Mesmo se você adicionar uma variável para rastrear o índice, o loop simples será mais fácil de ler.
Então, quando você deve usar
enumerateObjectsUsingBlock:
? Quando você está armazenando um bloco para executar mais tarde ou em vários lugares. É bom para quando você está realmente usando um bloco como uma função de primeira classe, em vez de uma substituição exagerada de um corpo de loop.fonte
enumerateObjectsUsingBlock:
será a mesma velocidade ou mais rápida que a enumeração rápida em todos os casos.for(... in ...)
usa enumeração rápida que requer que a coleção forneça alguma representação intermediária das estruturas de dados internas. Como você observa, provavelmente irrelevante.When you're storing a block to execute later or in multiple places. It's good for when you're actually using a block as a first-class function rather than an overwrought replacement for a loop body.
enumerateObjects...
na verdade, pode ser mais lento que a enumeração rápida com um loop. Eu fiz esse teste milhares de vezes; o corpo do bloco e ciclo eram os mesmos única linha de código:[(NSOperation *)obj cancel];
. As médias: loop enum rápido --[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009
e para o bloco --[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043
. Estranho que a diferença de horário seja tão grande e consistente, mas, obviamente, esse é um caso de teste muito específico.Embora essa pergunta seja antiga, as coisas não mudaram, a resposta aceita está incorreta.
A
enumerateObjectsUsingBlock
API não foi substituídafor-in
, mas para um caso de uso totalmente diferente:withOptions:
parâmetro)A Enumeração Rápida com
for-in
ainda é o método idiomático de enumerar uma coleção.A Enumeração Rápida se beneficia da brevidade do código, da legibilidade e das otimizações adicionais, que a tornam estranhamente rápida. Mais rápido que um loop for C antigo!
Um teste rápido conclui que no ano de 2014 no iOS 7,
enumerateObjectsUsingBlock
é consistentemente 700% mais lento que o for-in (com base nas iterações de 1 mm de uma matriz de 100 itens).O desempenho é uma preocupação prática real aqui?
Definitivamente não, com raras exceções.
O objetivo é demonstrar que há pouco benefício em usar
enumerateObjectsUsingBlock:
mais defor-in
sem uma boa razão. Não torna o código mais legível ... ou mais rápido ... ou seguro para threads. (outro equívoco comum).A escolha se resume à preferência pessoal. Para mim, a opção idiomática e legível vence. Nesse caso, isso é enumeração rápida usando
for-in
.Referência:
Resultados:
fonte
enumerateObjectsUsingBlock
é realmente 5X mais lenta. Aparentemente, isso ocorre devido a um pool de autorelease que envolve cada invocação do bloco, o que não acontece nofor in
caso.enumerateObjectsUsingBlock:
ainda é 4X mais lento no iPhone 6 iOS9 real, usando o Xcode 7.x para compilar.enumerate
sintaxe, porque ela se parece com um FP e não quer ouvir a análise de desempenho.enumerate:
Para responder à pergunta sobre desempenho, fiz alguns testes usando meu projeto de teste de desempenho . Eu queria saber qual das três opções para enviar uma mensagem para todos os objetos em uma matriz é a mais rápida.
As opções foram:
1) makeObjectsPerformSelector
2) enumeração rápida e envio regular de mensagens
3) enumerateObjectsUsingBlock e envio regular de mensagens
Acontece que makeObjectsPerformSelector foi o mais lento de longe. Demorou o dobro do tempo da enumeração rápida. E o enumerateObjectsUsingBlock foi o mais rápido, cerca de 15 a 20% mais rápido que a iteração rápida.
Portanto, se você estiver muito preocupado com o melhor desempenho possível, use enumerateObjectsUsingBlock. Mas lembre-se de que, em alguns casos, o tempo que leva para enumerar uma coleção é diminuído pelo tempo que leva para executar o código que você deseja que cada objeto execute.
fonte
É bastante útil usar enumerateObjectsUsingBlock como um loop externo quando você deseja interromper loops aninhados.
por exemplo
A alternativa é usar instruções goto.
fonte
Agradecemos a @bbum e @Chuck por iniciarem comparações abrangentes sobre desempenho. Fico feliz em saber que é trivial. Eu pareço ter ido com:
for (... in ...)
- como meu padrão ir. Mais intuitivo para mim, mais histórico de programação aqui do que qualquer preferência real - reutilização entre idiomas, menos digitação para a maioria das estruturas de dados devido ao preenchimento automático do IDE: P.enumerateObject...
- quando o acesso ao objeto e ao índice for necessário. E ao acessar estruturas que não são de matriz ou de dicionário (preferência pessoal)for (int i=idx; i<count; i++)
- para matrizes, quando eu preciso iniciar em um índice diferente de zerofonte