Помимо очевидных отличий:
- Используйте,
enumerateObjectsUsingBlock
когда вам нужен и индекс, и объект Не используйте,(я ошибся, см. Ответ bbum)enumerateObjectsUsingBlock
когда вам нужно изменить локальные переменные
Является ли в enumerateObjectsUsingBlock
целом считается лучше или хуже , если for (id obj in myArray)
бы также работать? Каковы преимущества / недостатки (например, более или менее эффективны)?
objective-c
multithreading
ios4
objective-c-blocks
Пол Уилер
источник
источник
Ответы:
В конечном счете, используйте любой шаблон, который вы хотите использовать, и он более естественен в контексте.
Хотя
for(... in ...)
это довольно удобно и синтаксически кратко,enumerateObjectsUsingBlock:
имеет ряд функций, которые могут или не могут оказаться интересными:enumerateObjectsUsingBlock:
будет так же быстро или быстрее, чем быстрое перечисление (for(... in ...)
используетNSFastEnumeration
поддержку для реализации перечисления). Быстрое перечисление требует перевода из внутреннего представления в представление для быстрого перечисления. Там есть над головой. Перечисление на основе блоков позволяет классу коллекции перечислять содержимое так же быстро, как и самый быстрый обход собственного формата хранения. Вероятно, не имеет значения для массивов, но это может иметь огромное значение для словарей.«Не используйте enumerateObjectsUsingBlock, когда вам нужно изменить локальные переменные» - не верно; Вы можете объявить своих местных как,
__block
и они будут доступны для записи в блоке.enumerateObjectsWithOptions:usingBlock:
поддерживает одновременное или обратное перечисление.в словарях перечисление на основе блоков - единственный способ получить ключ и значение одновременно.
Лично я пользуюсь
enumerateObjectsUsingBlock:
чаще, чемfor (... in ...)
, но - опять же - личным выбором.источник
enumerateObjectsUsingBlock
все еще значительно медленнее, чем быстрое перечисление массивов и множеств. Интересно, почему? iosdevelopertips.com/objective-c/...enumerateObjectsUsingBlock
заключают в себе каждый вызов блока с пулом автоматического выпуска (по крайней мере, начиная с OS X 10.10). Это объясняет разницу в производительности по сравнению с тем,for in
чего не происходит.Для простого перечисления, просто использование быстрого перечисления (то есть
for…in…
цикла) является более идиоматическим вариантом. Блочный метод может быть немного быстрее, но в большинстве случаев это не имеет большого значения - лишь немногие программы связаны с процессором, и даже в этом случае узким местом будет сам цикл, а не вычисления внутри.Простой цикл также читается более четко. Вот шаблон двух версий:
Даже если вы добавите переменную для отслеживания индекса, простой цикл будет легче читать.
Итак, когда вы должны использовать
enumerateObjectsUsingBlock:
? Когда вы храните блок для выполнения позже или в нескольких местах. Это хорошо, когда вы на самом деле используете блок в качестве первоклассной функции, а не замену тела цикла.источник
enumerateObjectsUsingBlock:
будет либо одинаковая скорость, либо быстрее, чем быстрое перечисление во всех случаях.for(... in ...)
использует быстрое перечисление, которое требует, чтобы коллекция обеспечивала некоторое временное представление внутренних структур данных. Как вы заметили, скорее всего, не имеет значения.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...
самом деле может быть медленнее, чем быстрое перечисление с циклом. Я провел этот тест несколько тысяч раз; тело блока и петли были такими же , одна строка кода:[(NSOperation *)obj cancel];
. Средние: быстрый цикл enum --[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009
и для блока --[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043
. Странно, что разница во времени настолько велика и постоянна, но, очевидно, это очень специфический тестовый пример.Хотя этот вопрос старый, все не изменилось, принятый ответ неверен.
enumerateObjectsUsingBlock
API не был предназначен , чтобы заменитьfor-in
, но совершенно иной вариант использования:withOptions:
параметра)Быстрое перечисление с помощью
for-in
- все еще идиоматический метод перечисления коллекции.Быстрое перечисление выигрывает от краткости кода, читабельности и дополнительных оптимизаций, которые делают его неестественно быстрым. Быстрее, чем старый цикл C!
Быстрый тест показывает, что в 2014 году на iOS 7
enumerateObjectsUsingBlock
он был на 700% медленнее, чем for-in (на основе 1-миллиметровых итераций массива из 100 элементов).Является ли производительность настоящей практической проблемой здесь?
Определенно нет, за редким исключением.
Смысл в том, чтобы продемонстрировать, что использование
enumerateObjectsUsingBlock:
чрезмерной пользы мало что даетfor-in
без веской причины. Это не делает код более читабельным ... или быстрее ... или поточно-ориентированным. (еще одно распространенное заблуждение).Выбор сводится к личным предпочтениям. Для меня идиоматичный и читабельный вариант выигрывает. В данном случае это быстрое перечисление
for-in
.Ориентир:
Полученные результаты:
источник
enumerateObjectsUsingBlock
на самом деле, в 5 раз медленнее. Это, по-видимому, связано с пулом автоматического выпуска, оборачивающим каждый вызов блока, чего не происходит в данномfor in
случае.enumerateObjectsUsingBlock:
что все еще в 4 раза медленнее на реальном iPhone 6 iOS9, используя Xcode 7.x для сборки.enumerate
синтаксис, потому что он похож на FP и не хочет слушать анализ производительности.enumerate:
Чтобы ответить на вопрос о производительности, я провел несколько тестов, используя свой проект тестирования производительности . Я хотел знать, какой из трех вариантов отправки сообщения всем объектам в массиве является самым быстрым.
Варианты были:
1) makeObjectsPerformSelector
2) быстрое перечисление и отправка обычного сообщения
3) enumerateObjectsUsingBlock и отправка обычного сообщения
Оказывается, что makeObjectsPerformSelector был самым медленным на сегодняшний день. Это заняло вдвое больше времени, чем быстрое перечисление. И enumerateObjectsUsingBlock был самым быстрым, он был примерно на 15-20% быстрее, чем быстрая итерация.
Поэтому, если вы очень заинтересованы в максимальной производительности, используйте enumerateObjectsUsingBlock. Но имейте в виду, что в некоторых случаях время, необходимое для перечисления коллекции, значительно меньше времени, которое требуется для выполнения любого кода, который вы хотите, чтобы каждый объект выполнял.
источник
Довольно полезно использовать enumerateObjectsUsingBlock в качестве внешнего цикла, когда вы хотите разорвать вложенные циклы.
например
Альтернативой является использование операторов goto.
источник
Спасибо @bbum и @Chuck за всестороннее сравнение производительности. Рад знать, что это тривиально. Я, кажется, пошел с:
for (... in ...)
- как мой goto по умолчанию. Более интуитивно понятный для меня, больше истории программирования здесь, чем любое реальное предпочтение - многоязычное повторное использование, меньше набирание текста для большинства структур данных благодаря автоматическому завершению IDE: P.enumerateObject...
- когда необходим доступ к объекту и индексу. И при доступе к структурам без массивов или словарей (персональные предпочтения)for (int i=idx; i<count; i++)
- для массивов, когда мне нужно начать с ненулевого индексаисточник