Вопрос

Я ищу стандартную идиому для перебора 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 в качестве аргумента опций.


10.4 или более ранняя версия

Стандартной идиомой для версий до 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 для перечисления массива в обратном направлении, без каких-либо других изменений кода.Если код итерации находится в методе, вы даже можете передать любой NSEnumerator и код даже не должен заботиться о что это повторяется.Далее, NSEnumerator (по крайней мере, те, которые предусмотрены кодом Apple) сохраняет перечисляемую коллекцию до тех пор, пока существует больше объектов, поэтому вам не нужно беспокоиться о том, как долго будет существовать автоматически выпущенный объект.

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


Другие советы

Для 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

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

// Создано Мехметом Актеном 02.05.2009.

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

#import <UIKit/UIKit.h>

@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

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

// Создано Мехметом Актеном 02.05.2009.

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

    #import "Array_Speed_TestViewController.h"
    #include <mach/mach.h>
    #include <mach/mach_time.h>

 @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<numberOfItems; i++) {

        total += [[nsArray objectAtIndex:i] floatValue];

    }

    [self displayResult:mach_absolute_time() - startTime];

}




// process using C Array

-(IBAction) doCArray:(id)sender {

    NSLog(@"doCArray: %@", sender);


    uint64_t start = mach_absolute_time();

    float total = 0;

    for(int i=0; i<numberOfItems; i++) {

        total += cArray[i];

    }

    [self displayResult:mach_absolute_time() - start];

}



// allocate NSArray and C Array 

-(void) allocateArrays {

    NSLog(@"allocateArrays");


    // allocate c array

    if(cArray) delete cArray;

    cArray = new float[numberOfItems];


    // allocate NSArray

    [nsArray release];

    nsArray = [[NSMutableArray alloc] initWithCapacity:numberOfItems];



    // fill with random values

    for(int i=0; i<numberOfItems; i++) {

        // add number to c array

        cArray[i] = random() * 1.0f/(RAND_MAX+1);


        // add number to NSArray

        NSNumber *number = [[NSNumber alloc] initWithFloat:cArray[i]];

        [nsArray addObject:number];

        [number release];

    }


}



// callback for when slider is changed

-(IBAction) sliderChanged:(id)sender {

    numberOfItems = sliderCount.value;

    NSLog(@"sliderChanged: %@, %i", sender, numberOfItems);


    NSString *str = [[NSString alloc] initWithFormat:@"%i items", numberOfItems];

    [labelCount setText:str];

    [str release];


    [self allocateArrays];

}



//cleanup

- (void)dealloc {

    [nsArray release];

    if(cArray) delete cArray;


    [super dealloc];

}


@end

От :memo.tv

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

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

[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

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

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

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

NSEнумератор

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

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

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

объектАтИндекс:перечисление

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

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, тебе это очень понадобится

Код взят из ЦельСахар

- (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);
}

Для Свифта

let arrayNumbers = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

// 1
for (index, value) in arrayNumbers.enumerated() {
    print(index, value)
    //... do somthing with array value and index
}


//2
for value in arrayNumbers {
    print(value)
    //... do somthing with array value
}

Сделай это :-

for (id object in array) 
{
        // statement
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top