Я ищу стандартную идиому для перебора NSArray. Мой код должен соответствовать OS X 10.4+.
источник
Я ищу стандартную идиому для перебора NSArray. Мой код должен соответствовать OS X 10.4+.
Обычно предпочтительный код для 10.5 + / iOS.
for (id object in array) {
// do something with object
}
Эта конструкция используется для перечисления объектов в коллекции, которая соответствует NSFastEnumeration
протоколу. Этот подход имеет преимущество в скорости, потому что он хранит указатели на несколько объектов (полученных с помощью одного вызова метода) в буфере и выполняет итерацию по ним, продвигаясь через буфер с использованием арифметики указателей. Это намного быстрее, чем звонить -objectAtIndex:
каждый раз через цикл.
Стоит также отметить, что, хотя технически вы можете использовать цикл for-in для пошагового выполнения NSEnumerator
, я обнаружил, что это сводит на нет практически все преимущества скорости быстрого перечисления. Причина в том, что NSEnumerator
реализация по умолчанию -countByEnumeratingWithState:objects:count:
помещает только один объект в буфер при каждом вызове.
Я сообщил об этом в radar://6296108
(Быстрое перечисление NSEnumerators вяло), но оно было возвращено как Не исправлено. Причина в том, что быстрое перечисление предварительно выбирает группу объектов, и если вы хотите перечислить только определенную точку в перечислителе (например, до тех пор, пока не будет найден конкретный объект или не выполнено условие) и использовать тот же перечислитель после разрыва цикла, часто бывает, что несколько объектов будут пропущены.
Если вы программируете для OS X 10.6 / iOS 4.0 и выше, у вас также есть возможность использовать блочные API для перечисления массивов и других коллекций:
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
Вы также можете использовать -enumerateObjectsWithOptions:usingBlock:
и передавать NSEnumerationConcurrent
и / или NSEnumerationReverse
в качестве аргумента параметров.
Стандартная идиома для pre-10.5 заключается в использовании NSEnumerator
цикла while и while, например:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
Я рекомендую держать это простым. Привязка себя к типу массива негибка, и предполагаемое увеличение скорости использования -objectAtIndex:
незначительно для улучшения с быстрым перечислением на 10,5+ в любом случае. (Быстрое перечисление фактически использует арифметику указателей на базовой структуре данных и устраняет большую часть накладных расходов при вызове метода.) Преждевременная оптимизация никогда не бывает хорошей идеей - она приводит к более сложному коду для решения проблемы, которая в любом случае не является вашим узким местом.
При использовании -objectEnumerator
вы очень легко переключаетесь на другую перечисляемую коллекцию (например NSSet
, ключи NSDictionary
, и т. Д.) Или даже переключаетесь -reverseObjectEnumerator
на перечисление массива в обратном порядке, и все это без каких-либо других изменений кода. Если код итерации находится в методе, вы можете даже передать любой, NSEnumerator
и коду не нужно даже заботиться о том, что он выполняет. Кроме того, NSEnumerator
(по крайней мере, предоставляемые кодом Apple) сохраняет коллекцию, которую она перечисляет, до тех пор, пока существует больше объектов, поэтому вам не нужно беспокоиться о том, как долго будет существовать автоматически выпущенный объект.
Возможно, самая большая вещь, от которой NSEnumerator
(или быстрое перечисление) защищает вас, - это изменчивая коллекция (массив или другое), изменяемая под вами без вашего ведома, пока вы ее перечисляете. Если вы обращаетесь к объектам по индексу, вы можете столкнуться со странными исключениями или ошибками «один за другим» (часто спустя много времени после возникновения проблемы), которые могут быть ужасающими для отладки. Перечисление с использованием одной из стандартных идиом имеет поведение «fast-fast», поэтому проблема (вызванная неправильным кодом) проявится сразу же, когда вы попытаетесь получить доступ к следующему объекту после того, как произошла мутация. Поскольку программы становятся более сложными и многопоточными или даже зависят от того, что может изменить сторонний код, хрупкий код перечисления становится все более проблематичным. Инкапсуляция и абстракция FTW! :-)
for (id object in array)
, есть ли способ также определить текущий индекс объекта в массиве, или нужно включить отдельный счетчик?for
цикл:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
Для OS X 10.4.x и предыдущих версий:
Для OS X 10.5.x (или iPhone) и выше:
источник
for (NSUInteger i = 0, count = [myArray count]; i < count; i++)
, вероятно, является наиболее эффективным и лаконичным для такого подхода.Результаты теста и исходный код приведены ниже (вы можете установить количество итераций в приложении). Время указывается в миллисекундах, и каждая запись является средним результатом выполнения теста 5-10 раз. Я обнаружил, что, как правило, с точностью до 2-3 значащих цифр, и после этого он будет меняться с каждым прогоном. Это дает погрешность менее 1%. Тест проводился на iPhone 3G, так как это целевая платформа, которая меня интересовала.
Классы, предоставляемые Cocoa для обработки наборов данных (NSDictionary, NSArray, NSSet и т. Д.), Предоставляют очень удобный интерфейс для управления информацией, не беспокоясь о бюрократии управления памятью, перераспределения и т. Д. Конечно, это обходится дорого, хотя , Я думаю, совершенно очевидно, что использование NSArray из NSNumbers будет медленнее, чем использование массива с плавающей точкой C для простых итераций, поэтому я решил провести несколько тестов, и результаты были довольно шокирующими! Я не ожидал, что это будет так плохо. Примечание: эти тесты проводятся на iPhone 3G, поскольку это целевая платформа, которая меня заинтересовала.
В этом тесте я делаю очень простое сравнение производительности произвольного доступа между C float * и NSArray из NSNumbers
Я создаю простой цикл, чтобы суммировать содержимое каждого массива и рассчитывать время, используя mach_absolute_time (). NSMutableArray занимает в среднем в 400 раз больше времени !! (не на 400 процентов, а в 400 раз дольше! это на 40000% дольше!).
Заголовок:
// Array_Speed_TestViewController.h
// Тест скорости массива
// Создано Мехмет Актен 05/02/2009.
// Copyright MSA Visuals Ltd. 2009. Все права защищены.
Реализация:
// Array_Speed_TestViewController.m
// Тест скорости массива
// Создано Мехмет Актен 05/02/2009.
// Copyright MSA Visuals Ltd. 2009. Все права защищены.
От: memo.tv
////////////////////
Доступно с момента появления блоков, это позволяет перебирать массив с блоками. Его синтаксис не так хорош, как быстрое перечисление, но есть одна очень интересная особенность: параллельное перечисление. Если порядок перечисления не важен и задания могут выполняться параллельно без блокировки, это может обеспечить значительное ускорение в многоядерной системе. Подробнее об этом в разделе одновременного перечисления.
/////////// NSFastEnumerator
Идея быстрого перечисления заключается в использовании быстрого доступа к массиву C для оптимизации итерации. Мало того, что он должен быть быстрее, чем традиционный NSEnumerator, но Objective-C 2.0 также обеспечивает очень краткий синтаксис.
/////////////////
NSEnumerator
Это форма внешней итерации: [myArray objectEnumerator] возвращает объект. Этот объект имеет метод nextObject, который мы можем вызывать в цикле, пока он не вернет ноль
/////////////////
objectAtIndex: перечисление
Использование цикла for, который увеличивает целое число, и запрос объекта с помощью [myArray objectAtIndex: index] является наиболее простой формой перечисления.
////////////// От: darkdust.net
источник
Три способа:
источник
Добавьте
each
метод в вашNSArray category
, вам это понадобится многоКод взят из ObjectiveSugar
источник
Вот как вы объявляете массив строк и перебираете их:
источник
Для Свифта
источник
Сделай это :-
источник