Outre les différences évidentes:
- À utiliser
enumerateObjectsUsingBlock
lorsque vous avez besoin à la fois de l'index et de l'objet Ne pas utiliser(je me suis trompé à ce sujet, voir la réponse de bbum)enumerateObjectsUsingBlock
lorsque vous devez modifier des variables locales
Est-ce enumerateObjectsUsingBlock
généralement considéré comme meilleur ou pire quand for (id obj in myArray)
fonctionnerait également? Quels sont les avantages / inconvénients (par exemple est-il plus ou moins performant)?
objective-c
multithreading
ios4
objective-c-blocks
Paul Wheeler
la source
la source
Réponses:
En fin de compte, utilisez le modèle que vous souhaitez utiliser et qui vient plus naturellement dans le contexte.
Bien qu'il
for(... in ...)
soit assez pratique et syntaxiquement bref, ilenumerateObjectsUsingBlock:
présente un certain nombre de fonctionnalités qui peuvent ou non s'avérer intéressantes:enumerateObjectsUsingBlock:
sera aussi rapide ou plus rapide que l'énumération rapide (for(... in ...)
utilise leNSFastEnumeration
support 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à-dedans. L'énumération basée sur des blocs permet à la classe de collection d'énumérer le contenu aussi rapidement que le parcours le plus rapide du format de stockage natif. Probablement pas pertinent 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 inscriptibles dans le bloc.enumerateObjectsWithOptions:usingBlock:
prend en charge l'énumération simultanée ou inversée.avec les dictionnaires, l'énumération par blocs est le seul moyen de récupérer simultanément la clé et la valeur.
Personnellement, j'utilise
enumerateObjectsUsingBlock:
plus souvent quefor (... in ...)
, mais - encore une fois - choix personnel.la source
enumerateObjectsUsingBlock
soit encore beaucoup plus lent que l'énumération rapide des tableaux et des ensembles. Je me demande pourquoi? iosdevelopertips.com/objective-c/…enumerateObjectsUsingBlock
encapsulent chaque appel du bloc avec un pool de libération automatique (à partir d'OS X 10.10 au moins). Cela explique la différence de performance par rapport àfor in
qui ne fait pas cela.Pour une énumération simple, utiliser simplement une énumération rapide (c'est-à-dire une
for…in…
boucle) est l'option la plus idiomatique. La méthode par blocs 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 processeur, et même dans ce cas, il est rare que la boucle elle-même plutôt que le calcul à l'intérieur soit un goulot d'étranglement.Une simple boucle se lit également plus clairement. Voici le passe-partout des deux versions:
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 plus tard ou à plusieurs endroits. C'est bien lorsque vous utilisez réellement un bloc comme fonction de première classe plutôt que comme un remplacement surchargé d'un corps de boucle.la source
enumerateObjectsUsingBlock:
sera soit la même vitesse, soit plus rapide que le dénombrement rapide dans tous les cas.for(... in ...)
utilise une énumération rapide qui oblige la collection à fournir une représentation intermédiaire des structures de données internes. Comme vous le remarquez, probablement hors de propos.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...
énumération peut être plus lente que rapide avec une boucle. J'ai effectué ce test plusieurs milliers de fois; le corps du bloc et de la boucle était la même ligne de code:[(NSOperation *)obj cancel];
. Les moyennes: boucle d'énumération rapide --[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009
et pour le bloc --[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043
. Bizarre que le décalage horaire soit si important et cohérent, mais, évidemment, il s'agit d'un cas de test très spécifique.Bien que cette question soit ancienne, les choses n'ont pas changé, la réponse acceptée est incorrecte.
L'
enumerateObjectsUsingBlock
API n'était pas censée remplacerfor-in
, mais pour un cas d'utilisation totalement différent:withOptions:
paramètre)L'énumération rapide avec
for-in
est toujours la méthode idiomatique d'énumération d'une collection.Fast Enumeration bénéficie de la brièveté du code, de la lisibilité et des optimisations supplémentaires qui le rendent anormalement rapide. Plus rapide qu'une ancienne boucle for C!
Un test rapide conclut qu'en 2014 sur iOS 7, il
enumerateObjectsUsingBlock
est systématiquement 700% plus lent que for-in (basé sur des itérations de 1 mm d'un tableau de 100 éléments).La performance est-elle ici une réelle préoccupation pratique?
Certainement pas, à de rares exceptions près.
Le but est de démontrer qu'il y a peu d'avantages à utiliser
enumerateObjectsUsingBlock:
overfor-in
sans une très bonne raison. Cela ne rend pas le code plus lisible ... ni plus rapide ... ni thread-safe. (une autre idée fausse courante).Le choix se résume à une préférence personnelle. Pour moi, l'option idiomatique et lisible gagne. Dans ce cas, c'est l'énumération rapide utilisant
for-in
.Référence:
Résultats:
la source
enumerateObjectsUsingBlock
est en fait 5 fois plus lent. Cela est apparemment dû à un pool de libération automatique enveloppant chaque invocation du bloc, ce qui ne se produit pas pour lefor in
cas.enumerateObjectsUsingBlock:
est toujours 4X plus lent sur le vrai iPhone 6 iOS9, en utilisant Xcode 7.x pour construire.enumerate
syntaxe parce qu'elle ressemble à FP et ne veulent pas écouter l'analyse des performances.enumerate:
Pour répondre à la question sur les performances, j'ai fait quelques tests en utilisant mon projet de test de performance . Je voulais savoir laquelle des trois options pour envoyer un message à tous les objets d'un tableau est la plus rapide.
Les options étaient:
1) makeObjectsPerformSelector
2) énumération rapide et envoi régulier de messages
3) enumerateObjectsUsingBlock et envoi régulier de messages
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 qu'une 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 pour énumérer une collection est réduit par le temps nécessaire pour exécuter le code que vous souhaitez que chaque objet exécute.
la source
Il est assez utile d'utiliser enumerateObjectsUsingBlock comme boucle externe lorsque vous souhaitez interrompre les boucles imbriquées.
par exemple
L'alternative consiste à utiliser des instructions goto.
la source
Merci à @bbum et @Chuck pour avoir lancé des comparaisons complètes sur les performances. Heureux de savoir que c'est trivial. Il me semble que je suis allé avec:
for (... in ...)
- par défaut goto. Plus intuitif pour moi, plus d'historique de programmation ici que n'importe quelle préférence réelle - réutilisation de plusieurs langages, moins de frappe pour la plupart des structures de données grâce à l'auto-complétion IDE: P.enumerateObject...
- lorsque l'accès à l'objet et à l'index est nécessaire. Et lors de l'accès à des structures non-tableau ou dictionnaire (préférence personnelle)for (int i=idx; i<count; i++)
- pour les tableaux, quand j'ai besoin de démarrer sur un index non nulla source