Neben den offensichtlichen Unterschieden:
- Verwenden
enumerateObjectsUsingBlock
Sie diese Option, wenn Sie sowohl den Index als auch das Objekt benötigen Nicht verwenden,(ich habe mich geirrt, siehe bbums Antwort)enumerateObjectsUsingBlock
wenn Sie lokale Variablen ändern müssen
Wird enumerateObjectsUsingBlock
allgemein als besser oder schlechter angesehen, wann for (id obj in myArray)
auch funktionieren würde? Was sind die Vor- und Nachteile (zum Beispiel ist es mehr oder weniger performant)?
objective-c
multithreading
ios4
objective-c-blocks
Paul Wheeler
quelle
quelle
Antworten:
Verwenden Sie letztendlich das Muster, das Sie verwenden möchten, und kommen Sie im Kontext natürlicher vor.
Obwohl
for(... in ...)
es recht praktisch und syntaktisch kurz ist,enumerateObjectsUsingBlock:
hat es eine Reihe von Funktionen, die sich als interessant erweisen können oder nicht:enumerateObjectsUsingBlock:
wird genauso schnell oder schneller sein als die schnelle Aufzählung (for(... in ...)
verwendet dieNSFastEnumeration
Unterstützung, um die Aufzählung zu implementieren). Eine schnelle Aufzählung erfordert eine Übersetzung von einer internen Darstellung in die Darstellung für eine schnelle Aufzählung. Es gibt Overhead darin. Durch die blockbasierte Aufzählung kann die Auflistungsklasse Inhalte so schnell auflisten wie die schnellste Durchquerung des nativen Speicherformats. Wahrscheinlich irrelevant für Arrays, aber für Wörterbücher kann es ein großer Unterschied sein."Verwenden Sie enumerateObjectsUsingBlock nicht, wenn Sie lokale Variablen ändern müssen" - nicht wahr; Sie können Ihre Einheimischen als deklarieren
__block
und sie werden im Block beschreibbar sein.enumerateObjectsWithOptions:usingBlock:
unterstützt entweder gleichzeitige oder umgekehrte Aufzählung.Bei Wörterbüchern ist die blockbasierte Aufzählung die einzige Möglichkeit, Schlüssel und Wert gleichzeitig abzurufen.
Persönlich benutze ich
enumerateObjectsUsingBlock:
öfter alsfor (... in ...)
, aber - wieder - persönliche Wahl.quelle
enumerateObjectsUsingBlock
immer noch deutlich langsamer zu sein als die schnelle Aufzählung für Arrays und Sets. Ich wundere mich warum? iosdevelopertips.com/objective-c/…enumerateObjectsUsingBlock
die jeden Aufruf des Blocks mit einem Autorelease-Pool umschließen (mindestens ab OS X 10.10). Dies erklärt den Leistungsunterschied, zufor in
dem dies nicht der Fall ist.Für eine einfache Aufzählung ist die einfache Verwendung einer schnellen Aufzählung (dh einer
for…in…
Schleife) die idiomatischere Option. Die Blockmethode mag geringfügig schneller sein, aber das spielt in den meisten Fällen keine große Rolle - nur wenige Programme sind CPU-gebunden, und selbst dann ist es selten, dass die Schleife selbst und nicht die Berechnung im Inneren ein Engpass ist.Eine einfache Schleife liest auch deutlicher. Hier ist das Boilerplate der beiden Versionen:
Selbst wenn Sie eine Variable hinzufügen, um den Index zu verfolgen, ist die einfache Schleife leichter zu lesen.
Also wann solltest du verwenden
enumerateObjectsUsingBlock:
? Wenn Sie einen Block speichern, um ihn später oder an mehreren Stellen auszuführen. Dies ist gut, wenn Sie einen Block tatsächlich als erstklassige Funktion verwenden und nicht als überarbeiteten Ersatz für einen Schleifenkörper.quelle
enumerateObjectsUsingBlock:
ist in allen Fällen entweder gleich schnell oder schneller als die schnelle Aufzählung.for(... in ...)
verwendet eine schnelle Aufzählung, bei der die Sammlung eine vorläufige Darstellung der internen Datenstrukturen liefern muss. Wie Sie bemerken, wahrscheinlich irrelevant.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...
die Aufzählung mit einer Schleife tatsächlich langsamer sein kann als die schnelle. Ich habe diesen Test mehrere tausend Mal durchgeführt. Der Hauptteil des Blocks und der Schleife bestand aus derselben einzigen Codezeile :[(NSOperation *)obj cancel];
. Die Durchschnittswerte: schnelle Aufzählungsschleife --[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009
und für den Block --[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043
. Seltsam, dass der Zeitunterschied so groß und konsistent ist, aber dies ist offensichtlich ein sehr spezifischer Testfall.Obwohl diese Frage alt ist, haben sich die Dinge nicht geändert, die die akzeptierte Antwort ist falsch.
Die
enumerateObjectsUsingBlock
API sollte nicht ersetzenfor-in
, sondern für einen völlig anderen Anwendungsfall:withOptions:
Parameters)Schnelle Aufzählung mit
for-in
ist immer noch die idiomatische Methode zum Auflisten einer Sammlung.Die schnelle Aufzählung profitiert von der Kürze des Codes, der Lesbarkeit und zusätzlichen Optimierungen , die ihn unnatürlich schnell machen. Schneller als eine alte C-for-Schleife!
Ein schneller Test kommt zu dem Schluss, dass das Jahr 2014 unter iOS 7
enumerateObjectsUsingBlock
durchweg 700% langsamer ist als das For-In (basierend auf 1-mm-Iterationen eines 100-Elemente-Arrays).Ist Leistung hier ein echtes praktisches Anliegen?
Auf keinen Fall, mit seltenen Ausnahmen.
Der Punkt ist zu zeigen, dass die Verwendung von
enumerateObjectsUsingBlock:
Overfor-in
ohne wirklich guten Grund wenig Nutzen bringt. Es macht den Code nicht lesbarer ... oder schneller ... oder threadsicherer. (ein weiteres häufiges Missverständnis).Die Wahl hängt von den persönlichen Vorlieben ab. Für mich gewinnt die idiomatische und lesbare Option. In diesem Fall ist dies die schnelle Aufzählung mit
for-in
.Benchmark:
Ergebnisse:
quelle
enumerateObjectsUsingBlock
tatsächlich 5-mal langsamer ist. Dies liegt anscheinend an einem Autorelease-Pool, der jeden Aufruf des Blocks umschließt, was in diesem Fall nicht derfor in
Fall ist.enumerateObjectsUsingBlock:
es auf echtem iPhone 6 iOS9 immer noch 4-mal langsamer ist und Xcode 7.x zum Erstellen verwendet.enumerate
Syntax einfach, weil sie sich FP-artig anfühlt und keine Leistungsanalyse hören möchte.enumerate:
Um die Frage nach der Leistung zu beantworten, habe ich einige Tests mit meinem Leistungstestprojekt durchgeführt . Ich wollte wissen, welche der drei Optionen zum Senden einer Nachricht an alle Objekte in einem Array die schnellste ist.
Die Optionen waren:
1) makeObjectsPerformSelector
2) schnelle Aufzählung und reguläres Senden von Nachrichten
3) enumerateObjectsUsingBlock & reguläres Senden von Nachrichten
Es stellt sich heraus, dass makeObjectsPerformSelector bei weitem am langsamsten war. Die schnelle Aufzählung dauerte doppelt so lange. Und enumerateObjectsUsingBlock war am schnellsten, es war ungefähr 15-20% schneller als schnelle Iteration.
Wenn Sie sich also große Sorgen um die bestmögliche Leistung machen, verwenden Sie enumerateObjectsUsingBlock. Beachten Sie jedoch, dass in einigen Fällen die Zeit, die zum Auflisten einer Sammlung benötigt wird, durch die Zeit, die zum Ausführen des Codes benötigt wird, den jedes Objekt ausführen soll, in den Schatten gestellt wird.
quelle
Es ist ziemlich nützlich, enumerateObjectsUsingBlock als äußere Schleife zu verwenden, wenn Sie verschachtelte Schleifen unterbrechen möchten.
z.B
Die Alternative ist die Verwendung von goto-Anweisungen.
quelle
Vielen Dank an @bbum und @Chuck für den Start umfassender Leistungsvergleiche. Ich bin froh zu wissen, dass es trivial ist. Ich scheine gegangen zu sein mit:
for (... in ...)
- als meine Standardeinstellung. Intuitiver für mich, mehr Programmierverlauf als jede echte Präferenz - sprachübergreifende Wiederverwendung, weniger Eingabe für die meisten Datenstrukturen aufgrund der automatischen Vervollständigung der IDE: P.enumerateObject...
- wenn Zugriff auf Objekt und Index benötigt wird. Und beim Zugriff auf Nicht-Array- oder Wörterbuchstrukturen (persönliche Präferenz)for (int i=idx; i<count; i++)
- für Arrays, wenn ich mit einem Index ungleich Null beginnen mussquelle