提问



我正在寻找标准的习惯来迭代NSArray。我的代码需要适合OS X 10.4+。

最佳参考


10.5 +/iOS的一般首选代码。


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


此构造用于枚举符合[NSFastEnumeration protocol](Cocoa Reference)的集合中的对象。这种方法具有速度优势,因为它存储指向缓冲区中的多个对象(通过单个方法调用获得)的指针,并通过使用指针算法推进缓冲区来迭代它们。这比通过循环调用-objectAtIndex:更快 。[37]


同样值得注意的是,虽然你在技术上可以使用for-in循环来逐步执行NSEnumerator,但我发现这几乎消除了快速枚举的所有速度优势。原因是-countByEnumeratingWithState:objects:count:的默认NSEnumerator实现在每次调用时只在缓冲区中放置一个对象。


我在radar://6296108中报道了这一点(NSEnumerators的快速枚举是缓慢的),但它被归还为Not To Be Fixed。原因是快速枚举预取一组对象,如果你只想枚举枚举器中的给定点(例如,直到找到特定对象,或满足条件)并在爆发后使用相同的枚举器在循环中,通常会跳过几个对象。


如果您要编写OS X 10.6/iOS 4.0及更高版本的代码,您还可以选择使用基于块的API枚举数组和其他集合:


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


您也可以使用-enumerateObjectsWithOptions:usingBlock:并传递NSEnumerationConcurrent和/或NSEnumerationReverse作为options参数。





10.4或更早



10.5之前的标准习惯是使用NSEnumerator和while循环,如下所示:


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


我建议保持简单。将自己绑定到阵列类型是不灵活的,并且使用-objectAtIndex:所声称的速度增加对于10.5+上的快速枚举的改进是微不足道的。 (快速枚举实际上在底层数据结构上使用指针算法,并删除了大部分方法调用开销。)过早优化从来都不是一个好主意 - 它会导致代码更加混乱,无法解决不是瓶颈的问题。


使用 -objectEnumerator 时,您可以轻松更改为另一个可枚举的集合(如NSSetNSDictionary中的键等),甚至可以切换到< strong> -reverseObjectEnumerator 向后枚举一个数组,所有没有其他代码更改。如果迭代代码在一个方法中,你甚至可以传入任何NSEnumerator并且代码甚至不必关心它正在迭代什么。此外,只要有更多对象,NSEnumerator(至少由Apple代码提供的那些)保留它所枚举的集合,因此您不必担心自动释放对象存在多长时间。


也许NSEnumerator(或快速枚举)保护你的最大的事情就是在你不知情的情况下在你的下面有一个可变的集合(数组或其他)改变。如果你通过索引访问对象,你可以遇到奇怪的异常或逐个错误(通常在问题发生很久之后),这可能是可怕的调试。使用标准习语之一的枚举有一个快速失败的行为,所以问题(由不正确的代码引起)将在您发生突变后尝试访问下一个对象时立即显现出来。随着程序变得更加复杂和多线程,甚至依赖于第三方代码可能修改的内容,脆弱的枚举代码变得越来越成问题。封装和抽象FTW!:-)




其它参考1


对于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
}

其它参考2


测试结果和源代码如下(您可以在应用程序中设置迭代次数)。时间以毫秒为单位,每个条目是运行测试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等)为管理信息提供了一个非常好的界面,而不必担心内存管理,重新分配等的官僚作风。当然,这确实需要付出代价。 。我认为很明显,对于简单的迭代,使用NSNumber的NSArray会比C数组的浮点慢,所以我决定做一些测试,结果非常令人震惊!我没想到它这是坏事。注意:这些测试是在iPhone 3G上进行的,因为它是我感兴趣的目标平台。


在这个测试中,我在NS float的C float *和NSArray之间进行了非常简单的随机访问性能比较


我创建了一个简单的循环来总结每个数组的内容,并使用mach_absolute_time()计算它们的时间。 NSMutableArray平均需要400倍!! (不是400%,只是400倍!这要长40,000%!)。


标题:


//Array_Speed_TestViewController.h


//阵列速度测试


//由Mehmet Akten于05/02/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


//阵列速度测试


//由Mehmet Akten于05/02/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];
}


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


NSEnumerator


这是一种外部迭代形式:[[myArray objectEnumerator]]返回一个对象。这个对象有一个方法nextObject,我们可以在循环中调用它直到它返回nil


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


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


objectAtIndex:枚举


使用for循环增加一个整数并使用[[myArray objectAtIndex:index]]查询对象是最基本的枚举形式。


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


//////////////
来自:darkdust.net

其它参考3


这三种方式是:


        //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.自动完成时忘记编写迭代包络。


其它参考4


NSArray category中添加each方法,你会需要它很多


代码取自ObjectiveSugar [38]


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

其它参考5


以下是如何声明字符串数组并迭代它们的方法:


NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];

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

其它参考6


做这个 :-


for (id object in array) 
{
        // statement
}