Как перебирать NSArray?

Я ищу стандартную идиому для итерации по NSArray. Мой код должен быть подходящим для OS X 10.4+.

7 Solutions collect form web for “Как перебирать NSArray?”

Обычно предпочтительный код для 10,5 + / iOS.

for (id object in array) { // do something with object } 

Эта конструкция используется для enums объектов в коллекции, которая соответствует [NSFastEnumeration protocol] ( Cocoa Reference ). Этот подход имеет преимущество в скорости, поскольку он хранит указатели на несколько объектов (полученных посредством одного вызова метода) в буфере и выполняет итерацию через них, продвигаясь через буфер, используя арифметику указателя. Это намного быстрее, чем вызов -objectAtIndex: каждый раз через цикл.

Также стоит отметить, что, хотя вы технически можете использовать цикл for-in для NSEnumerator через NSEnumerator , я обнаружил, что это сводит на нет практически все преимущества скорости быстрого enums. Причина в том, что реализация -countByEnumeratingWithState:objects:count: по умолчанию для -countByEnumeratingWithState:objects:count: помещает только один объект в буфер для каждого вызова.

Я сообщил об этом в radar://6296108 (Быстрое перечисление NSEnumerators вяло), но оно было возвращено как «Не исправлено». Причина в том, что быстрое перечисление предварительно задает группу объектов, и если вы хотите перечислить только определенную точку в перечислителе (например, до тех пор, пока не будет найден какой-либо конкретный объект или не будет выполнено условие) и используйте один и тот же счетчик после разрыва цикла, часто бывает, что несколько объектов будут пропущены.

Если вы кодируете OS X 10.6 / iOS 4.0 и выше, у вас также есть возможность использовать блочные API для enums массивов и других коллекций:

 [array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) { // do something with object }]; 

Вы также можете использовать -enumerateObjectsWithOptions:usingBlock: и передать NSEnumerationConcurrent и / или NSEnumerationReverse в качестве аргумента options.


10.4 или ранее

Стандартная идиома для pre-10.5 заключается в использовании NSEnumerator и while цикла, например:

 NSEnumerator *e = [array objectEnumerator]; id object; while (object = [e nextObject]) { // do something with object } 

Я рекомендую держать его простым. Привязка к типу массива негибкая, и предполагаемое увеличение скорости использования -objectAtIndex: незначительно для улучшения с быстрым перечислением на 10.5+ в любом случае. (Быстрое перечисление фактически использует арифметику указателя на базовой структуре данных и удаляет большую часть служебных данных вызова метода.) Преждевременная оптимизация никогда не является хорошей идеей – это приводит к тому, что более грязный код разрешает проблему, которая не является вашим узким местом в любом случае.

При использовании -objectEnumerator вы очень легко переходите на другую перечислимую коллекцию (например, NSSet , ключи в NSDictionary и т. Д.) Или даже переключаетесь на -reverseObjectEnumerator для enums массива назад, без каких-либо других изменений кода. Если код итерации находится в методе, вы можете даже передать любой NSEnumerator и код даже не должен заботиться о том, что он итерирует. Кроме того, NSEnumerator (по крайней мере, предоставленный кодом Apple) сохраняет список, который он перечисляет, если есть больше объектов, поэтому вам не нужно беспокоиться о том, как долго будет существовать объект с автореализацией.

Возможно, самое большое, что NSEnumerator (или быстрое перечисление) защищает вас от наличия изменяемой коллекции (массив или иначе), изменяется под вами без вашего ведома, пока вы перечисляете ее. Если вы обращаетесь к объектам по индексу, вы можете столкнуться с странными исключениями или по отдельности (часто спустя много времени после возникновения проблемы), которые могут быть ужасающими для отладки. Перечисление с использованием одного из стандартных идиом имеет «неудачное» поведение, поэтому проблема (вызванная неправильным кодом) проявится сразу же при попытке получить доступ к следующему объекту после возникновения мутации. Поскольку программы становятся более сложными и многопоточными или даже зависят от того, что может модифицировать сторонний код, хрупкий код enums становится все более проблематичным. Инкапсуляция и абстракция FTW! 🙂


Для OS X 10.4.x и предыдущих:

  int i; for (i = 0; i < [myArray count]; i++) { id myArrayElement = [myArray objectAtIndex:i]; ...do something useful with myArrayElement } 

Для OS X 10.5.x (или iPhone) и за его пределами:

 for (id myArrayElement in myArray) { ...do something useful with myArrayElement } 

Ниже приведены результаты теста и исходного кода (вы можете установить количество итераций в приложении). Время в миллисекундах, и каждая запись является средним результатом запуска теста 5-10 раз. Я обнаружил, что в целом он точен до 2-3 значащих цифр, и после этого он будет меняться при каждом прогоне. Это дает погрешность менее 1%. Тест проходил на iPhone 3G, так как это была целевая платформа, в которой меня интересовало.

 numberOfItems NSArray (ms) C Array (ms) Ratio 100 0.39 0.0025 156 191 0.61 0.0028 218 3,256 12.5 0.026 481 4,789 16 0.037 432 6,794 21 0.050 420 10,919 36 0.081 444 19,731 64 0.15 427 22,030 75 0.162 463 32,758 109 0.24 454 77,969 258 0.57 453 100,000 390 0.73 534 

Классы, предоставляемые Cocoa для обработки наборов данных (NSDictionary, NSArray, NSSet и т. Д.), Обеспечивают очень приятный интерфейс для управления информацией, не беспокоясь о бюрократии управления памятью, перераспределения и т. Д. Конечно, это действительно дорого , Я думаю, что довольно очевидно, что использование NSArray NSNumbers будет медленнее, чем C-массив с плавающей точкой для простых итераций, поэтому я решил сделать некоторые тесты, и результаты были довольно шокирующими! Я не ожидал, что это будет так плохо. Примечание: эти тесты проводятся на iPhone 3G, так как это целевая платформа, в которой меня интересовало.

В этом тесте я делаю очень простое сравнение производительности произвольного доступа между C float * и NSArray из NSNumbers

Я создаю простой цикл, чтобы суммировать содержимое каждого массива и время их использования с помощью mach_absolute_time (). NSMutableArray занимает в среднем 400 раз больше! (не 400 процентов, а в 400 раз больше!), что на 40 000% больше!).

Заголовок:

// Array_Speed_TestViewController.h

// Тест скорости массива

// Создано Mehmet Akten 05/02/2009.

// Авторское право MSA Visuals Ltd. 2009. Все права защищены.

 #import  @interface Array_Speed_TestViewController : UIViewController { int numberOfItems; // number of items in array float *cArray; // normal c array NSMutableArray *nsArray; // ns array double machTimerMillisMult; // multiplier to convert mach_absolute_time() to milliseconds IBOutlet UISlider *sliderCount; IBOutlet UILabel *labelCount; IBOutlet UILabel *labelResults; } -(IBAction) doNSArray:(id)sender; -(IBAction) doCArray:(id)sender; -(IBAction) sliderChanged:(id)sender; @end 

Реализация:

// Array_Speed_TestViewController.m

// Тест скорости массива

// Создано Mehmet Akten 05/02/2009.

// Авторское право MSA Visuals Ltd. 2009. Все права защищены.

  #import "Array_Speed_TestViewController.h" #include  #include  @implementation Array_Speed_TestViewController // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { NSLog(@"viewDidLoad"); [super viewDidLoad]; cArray = NULL; nsArray = NULL; // read initial slider value setup accordingly [self sliderChanged:sliderCount]; // get mach timer unit size and calculater millisecond factor mach_timebase_info_data_t info; mach_timebase_info(&info); machTimerMillisMult = (double)info.numer / ((double)info.denom * 1000000.0); NSLog(@"machTimerMillisMult = %f", machTimerMillisMult); } // pass in results of mach_absolute_time() // this converts to milliseconds and outputs to the label -(void)displayResult:(uint64_t)duration { double millis = duration * machTimerMillisMult; NSLog(@"displayResult: %f milliseconds", millis); NSString *str = [[NSString alloc] initWithFormat:@"%f milliseconds", millis]; [labelResults setText:str]; [str release]; } // process using NSArray -(IBAction) doNSArray:(id)sender { NSLog(@"doNSArray: %@", sender); uint64_t startTime = mach_absolute_time(); float total = 0; for(int i=0; i 

От: memo.tv

////////////////////

Доступный с момента введения блоков, это позволяет итерировать массив с блоками. Его синтаксис не так хорош, как быстрое перечисление, но есть одна очень интересная функция: параллельное перечисление. Если порядок enums не важен, и задания могут выполняться параллельно без блокировки, это может обеспечить значительное ускорение в многоядерной системе. Подробнее об этом в разделе параллельной переписи.

 [myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) { [self doSomethingWith:object]; }]; [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [self doSomethingWith:object]; }]; 

/////////// NSFastEnumerator

Идея быстрого enums - использовать быстрый доступ к массиву C для оптимизации итерации. Мало того, что он должен быть быстрее, чем традиционный NSEnumerator, но Objective-C 2.0 также обеспечивает очень сжатый синтаксис.

 id object; for (object in myArray) { [self doSomethingWith:object]; } 

/////////////////

NSEnumerator

Это форма внешней итерации: [myArray objectEnumerator] возвращает объект. Этот объект имеет метод nextObject, который мы можем вызвать в цикле, пока он не вернет nil

 NSEnumerator *enumerator = [myArray objectEnumerator]; id object; while (object = [enumerator nextObject]) { [self doSomethingWith:object]; } 

/////////////////

objectAtIndex: перечисление

Использование цикла for, которое увеличивает целое число и запрашивает объект с использованием [myArray objectAtIndex: index], является самой основной формой enums.

 NSUInteger count = [myArray count]; for (NSUInteger index = 0; index < count ; index++) { [self doSomethingWith:[myArray objectAtIndex:index]]; } 

////////////// От: darkdust.net

Три способа:

  //NSArray NSArray *arrData = @[@1,@2,@3,@4]; // 1.Classical for (int i=0; i< [arrData count]; i++){ NSLog(@"[%d]:%@",i,arrData[i]); } // 2.Fast iteration for (id element in arrData){ NSLog(@"%@",element); } // 3.Blocks [arrData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"[%lu]:%@",idx,obj); // Set stop to YES in case you want to break the iteration }]; 
  1. Является самым быстрым способом исполнения, и 3. с автозаполнением забывают о создании итерационного конверта.

Добавьте each метод в свою NSArray category , вам понадобится много

Код, взятый из ObjectiveSugar

 - (void)each:(void (^)(id object))block { [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { block(obj); }]; } 

Вот как вы объявляете массив строк и перебираете их:

 NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"]; for (int i = 0; i < [langs count]; i++) { NSString *lang = (NSString*) [langs objectAtIndex:i]; NSLog(@"%@, ",lang); } 

Сделай это :-

 for (id object in array) { // statement } 
Давайте будем гением компьютера.