Quand utiliser enumerateObjectsUsingBlock contre pour

Outre les différences évidentes:

  • Utilisez enumerateObjectsUsingBlock lorsque vous avez besoin à la fois de l’index et de l’object
  • N’utilisez pas enumerateObjectsUsingBlock lorsque vous devez modifier des variables locales (je me suis trompé à ce sujet, voir la réponse de bbum)

for (id obj in myArray) est enumerateObjectsUsingBlock généralement considéré comme meilleur ou pire for (id obj in myArray) fonctionne également? Quels sont les avantages / inconvénients (par exemple, est-il plus ou moins performant)?

En fin de compte, utilisez le modèle que vous souhaitez utiliser et intervient plus naturellement dans le contexte.

Alors que for(... in ...) est assez pratique et syntaxiquement bref, enumerateObjectsUsingBlock: possède un certain nombre de fonctionnalités qui peuvent ou peuvent ne pas être intéressantes:

  • enumerateObjectsUsingBlock: sera aussi rapide ou plus rapide que l’énumération rapide ( for(... in ...) utilise le support NSFastEnumeration pour implémenter l’énumération). L’énumération rapide nécessite la traduction d’une représentation interne vers la représentation pour une énumération rapide. Il y a des frais généraux. L’énumération basée sur les blocs permet à la classe de collection d’énumérer les contenus aussi rapidement que la traversée la plus rapide du format de stockage natif. Probablement sans intérêt pour les tableaux, mais cela peut être une énorme différence pour les dictionnaires.

  • “N’utilisez pas enumerateObjectsUsingBlock lorsque vous devez modifier des variables locales” – pas vrai; vous pouvez déclarer vos sections locales comme __block et elles seront accessibles en écriture dans le bloc.

  • enumerateObjectsWithOptions:usingBlock: prend en charge l’énumération simultanée ou inverse.

  • avec les dictionnaires, l’énumération par blocs est la seule façon de récupérer la clé et la valeur simultanément.

Personnellement, j’utilise enumerateObjectsUsingBlock: plus souvent que for (... in ...) , mais – encore une fois – choix personnel.

Pour une énumération simple, utiliser une énumération rapide (c.- for…in… boucle for…in… ) est l’option la plus idiomatique. La méthode du bloc peut être légèrement plus rapide, mais cela n’a pas beaucoup d’importance dans la plupart des cas – peu de programmes sont liés au CPU, et même dans ce cas, il est rare que la boucle plutôt que le calcul interne soit un goulot d’étranglement.

Une simple boucle se lit également plus clairement. Voici le standard des deux versions:

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

Même si vous ajoutez une variable pour suivre l’index, la boucle simple est plus facile à lire.

Alors, quand devriez-vous utiliser enumerateObjectsUsingBlock: 😕 Lorsque vous stockez un bloc à exécuter ultérieurement ou à plusieurs endroits. C’est bon pour les cas où vous utilisez un bloc comme une fonction de première classe plutôt que pour remplacer un corps de boucle.

Bien que cette question soit ancienne, les choses n’ont pas changé, la réponse acceptée est incorrecte.

L’API enumerateObjectsUsingBlock n’était pas destinée à remplacer le for-in , mais pour un cas d’utilisation totalement différent:

  • Il permet l’application d’une logique non locale arbitraire. c’est-à-dire que vous n’avez pas besoin de savoir ce que fait le bloc pour l’utiliser sur un tableau.
  • Énumération concurrente pour les grandes collections ou les calculs lourds (en utilisant le paramètre withOptions:

Le dénombrement rapide avec for-in rest la méthode idiomatique pour énumérer une collection.

Fast Enumeration bénéficie de la brièveté du code, de la lisibilité et d’ optimisations supplémentaires qui le rendent anormalement rapide. Plus rapide qu’un vieux C pour la boucle!

Un test rapide conclut qu’en 2014, sur iOS 7, enumerateObjectsUsingBlock est systématiquement plus lent de 700% (basé sur des itérations de 1 mm sur un tableau de 100 éléments).

La performance est-elle un réel problème pratique ici?

Certainement pas, sauf rares exceptions.

Le but est de démontrer qu’il n’ya que peu d’avantages à utiliser enumerateObjectsUsingBlock: over for-in sans raison valable. Cela ne rend pas le code plus lisible … ou plus rapide … ou thread-safe. (une autre idée fausse commune).

Le choix se résume à une préférence personnelle. Pour moi, l’option idiomatique et lisible gagne. Dans ce cas, Fast Enumeration utilise for-in .

Référence:

 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 (NSSsortingng *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:^(NSSsortingng *s, NSUInteger idx, BOOL *stop) { length = s.length; }]; } NSLog(@"Enum %llu", mach_absolute_time()-b1); 

Résultats:

 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 

Pour répondre à la question sur la performance, j’ai effectué des tests en utilisant mon projet de test de performance . Je voulais savoir laquelle des trois options pour envoyer un message à tous les objects d’un tableau est la plus rapide.

Les options étaient les suivantes:

1) makeObjectsPerformSelector

 [arr makeObjectsPerformSelector:@selector(_stubMethod)]; 

2) énumération rapide et envoi de message régulier

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

3) enumerateObjectsUsingBlock & envoi de message régulier

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

Il s’avère que makeObjectsPerformSelector était de loin le plus lent. Il a fallu deux fois plus de temps que le dénombrement rapide. Et enumerateObjectsUsingBlock était le plus rapide, il était environ 15 à 20% plus rapide que l’itération rapide.

Donc, si vous êtes très préoccupé par les meilleures performances possibles, utilisez enumerateObjectsUsingBlock. Mais gardez à l’esprit que dans certains cas, le temps nécessaire à l’énumération d’une collection est réduit par le temps nécessaire à l’exécution du code que chaque object doit exécuter.

Il est assez utile d’utiliser enumerateObjectsUsingBlock comme une boucle externe lorsque vous voulez casser des boucles nestedes.

par exemple

 [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; } } } }]; 

L’alternative utilise les instructions goto.

Merci à @bbum et @Chuck pour lancer des comparaisons complètes sur les performances. Heureux de savoir que c’est sortingvial. Je semble avoir été avec:

  • for (... in ...) – comme mon goto par défaut. Plus intuitif pour moi, plus d’historique de programmation ici que de préférence réelle – réutilisation de plusieurs langues, moins de typage pour la plupart des structures de données en raison de la complétion automatique de l’IDE: P.

  • enumerateObject... – lorsque l’access à l’object et à l’index est requirejs. Et lors de l’access à des structures non-tableau ou dictionnaire (préférence personnelle)

  • for (int i=idx; i - pour les tableaux, lorsque je dois démarrer sur un index non nul