Quando usar enumerateObjectsUsingBlock vs.

Além das diferenças óbvias:

  • Use enumerateObjectsUsingBlock quando precisar do índice e do object
  • Não use enumerateObjectsUsingBlock quando precisar modificar variables ​​locais (eu estava errado sobre isso, veja a resposta do bbum)

É 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 de desempenho)?

Em última análise, use o padrão que você quer usar e vem mais naturalmente no contexto.

Embora for(... in ...) seja bastante conveniente e sintaticamente breve, enumerateObjectsUsingBlock: possui vários resources 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 o suporte NSFastEnumeration para implementar enumeração). Enumeração rápida requer conversão de uma representação interna para a representação para enumeração rápida. Há sobrecarga nisso. A enumeração baseada em blocos permite que a class de coleção enumere o conteúdo tão rapidamente quanto o percurso mais rápido do formato de armazenamento nativo. Provavelmente irrelevante para matrizes, mas pode ser uma grande diferença para os dictionarys.

  • “Não use enumerateObjectsUsingBlock quando precisar modificar variables ​​locais” – não true; você pode declarar seus locais como __block e eles serão graváveis ​​no bloco.

  • enumerateObjectsWithOptions:usingBlock: suporta enumeração simultânea ou inversa.

  • com dictionarys, a enumeração baseada em blocos é a única maneira de recuperar a chave e o valor simultaneamente.

Pessoalmente, eu uso enumerateObjectsUsingBlock: mais frequentemente do que for (... in ...) , mas – novamente – escolha pessoal.

Para enumeração simples, simplesmente usando enumeração rápida (ou seja, um for…in… loop) é a opção mais idiomática. O método de bloco pode ser marginalmente mais rápido, mas isso não importa muito na maioria dos casos – poucos programas são ligados à CPU e, mesmo assim, é raro que o loop em si, em vez do cálculo interno, seja um gargalo.

Um simples loop também lê mais claramente. Aqui está o clichê das duas versões:

 for (id x in y){ } [y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){ }]; 

Mesmo se você adicionar uma variável para rastrear o índice, o loop simples é 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 function de primeira class, em vez de um substituto overwrought para um corpo de loop.

Embora essa questão seja antiga, as coisas não mudaram, a resposta aceita está incorreta.

A enumerateObjectsUsingBlock API não foi feita para replace o for-in , mas para um caso de uso totalmente diferente:

  • Permite a aplicação de lógica arbitrária e não local. ou seja, você não precisa saber o que o bloco faz para usá-lo em uma matriz.
  • Enumeração concorrente para collections grandes ou computação pesada (usando o parâmetro withOptions:

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, o que a torna incrivelmente rápida. Mais rápido que um velho C for-loop!

Um teste rápido conclui que, no ano de 2014, no iOS 7, enumerateObjectsUsingBlock é consistentemente 700% mais lento do 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 ponto é demonstrar que há pouco benefício em usar enumerateObjectsUsingBlock: over for-in sem uma boa razão. Não torna o código mais legível … ou mais rápido … ou thread-safe. (outro equívoco comum).

A escolha se resume à preferência pessoal. Para mim, a opção idiomática e legível ganha. Neste caso, isso é Enumeração Rápida usando for-in .

Referência:

 NSMutableArray *arr = [NSMutableArray array]; for (int i = 0; i < 100; i++) { arr[i] = [NSString stringWithFormat:@"%d", i]; } int i; __block NSUInteger length; i = 1000 * 1000; uint64_t a1 = mach_absolute_time(); while (--i > 0) { for (NSString *s in arr) { length = s.length; } } NSLog(@"For-in %llu", mach_absolute_time()-a1); i = 1000 * 1000; uint64_t b1 = mach_absolute_time(); while (--i > 0) { [arr enumerateObjectsUsingBlock:^(NSString *s, NSUInteger idx, BOOL *stop) { length = s.length; }]; } NSLog(@"Enum %llu", mach_absolute_time()-b1); 

Resultados:

 2014-06-11 14:37:47.717 Test[57483:60b] For-in 1087754062 2014-06-11 14:37:55.492 Test[57483:60b] Enum 7775447746 

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 objects em uma matriz é a mais rápida.

As opções eram:

1) makeObjectsPerformSelector

 [arr makeObjectsPerformSelector:@selector(_stubMethod)]; 

2) enumeração rápida e envio regular de mensagens

 for (id item in arr) { [item _stubMethod]; } 

3) enumerateObjectsUsingBlock e envio regular de mensagens

 [arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [obj _stubMethod]; }]; 

Acontece que makeObjectsPerformSelector foi o mais lento de longe. Demorou o dobro da enumeração rápida. E enumerateObjectsUsingBlock foi o mais rápido, foi cerca de 15-20% mais rápido que a iteração rápida.

Então, se você está muito preocupado com o melhor desempenho possível, use enumerateObjectsUsingBlock. Mas lembre-se de que, em alguns casos, o tempo necessário para enumerar uma coleção é diminuído pelo tempo necessário para executar o código que você deseja que cada object execute.

É bastante útil usar enumerateObjectsUsingBlock como um loop externo quando você deseja quebrar loops nesteds.

por exemplo

 [array1 enumerateObjectsUsingBlock:^(id obj1, NSUInteger idx, BOOL * _Nonnull stop) { for(id obj2 in array2) { for(id obj3 in array3) { if(condition) { // break ALL the loops! *stop = YES; return; } } } }]; 

A alternativa é usar instruções goto.

Graças a @bbum e @Chuck por iniciar comparações abrangentes sobre desempenho. Fico feliz em saber que é trivial. Eu pareço ter ido com:

  • for (... in ...) – como meu padrão goto. Mais intuitivo para mim, mais histórico de programação aqui do que qualquer preferência real – reutilização de linguagem cruzada, menos digitação para a maioria das estruturas de dados devido ao preenchimento automático do IDE: P.

  • enumerateObject... – quando o access ao object e índice é necessário. E ao acessar estruturas não-matriz ou dictionary (preferência pessoal)

  • for (int i=idx; i - para matrizes, quando preciso iniciar em um índice diferente de zero