我如何迭代NSArray?

我正在寻找标准惯用法来遍历NSArray。 我的代码需要适用于OS X 10.4+。


10.5 + / iOS的通用首选代码。

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

该构造用于枚举符合[NSFastEnumeration protocol] (Cocoa Reference)的集合中的对象。 这种方法具有速度优势,因为它将指向几个对象(通过单一方法调用获得)的指针存储在缓冲区中,并通过使用指针算法在缓冲区中前进来遍历它们。 这比调用-objectAtIndex:每次通过循环。

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

我在radar://6296108报道了这个radar://6296108radar://6296108快速枚举很慢),但是它被返回为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:到一个数组类型是不灵活的,并且使用-objectAtIndex:速度增加无关紧要,因为无论如何10.5+都能快速枚举。 (快速枚举实际上是在底层数据结构上使用指针算术,并且消除了大部分的方法调用开销。)过早优化从来不是一个好主意 - 它会导致更复杂的代码来解决一个并非瓶颈的问题。

当使用-objectEnumerator ,你可以很容易地更改为另一个可枚举集合(例如NSSetNSDictionary键等),或者甚至切换到-reverseObjectEnumerator以向后枚举数组,所有这些都不需要其他代码更改。 如果迭代代码在方法中,那么甚至可以传入任何NSEnumerator ,代码甚至不必关心迭代的内容。 此外,只要有更多的对象, NSEnumerator (至少由Apple代码提供的那些)保留它正在枚举的集合,因此您不必担心自动释放对象存在多久。

也许NSEnumerator (或快速枚举)保护你的最重要的事情是在枚举它的时候,在你不知情的情况下在你的底下有一个可变集合(数组或其他)。 如果通过索引访问对象,则可能会遇到奇怪的异常或逐个错误(通常在问题发生后很长时间),这些错误对于调试可能会非常可怕。 枚举使用其中一种标准惯用语具有“快速失败”行为,因此,当您尝试在发生变化后访问下一个对象时,问题(由不正确的代码引起)将立即显现。 随着程序变得更加复杂和多线程,甚至依赖于第三方代码可能修改的内容,脆弱的枚举代码变得越来越成问题。 封装和抽象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等)为管理信息提供了一个非常好的界面,而不必担心内存管理,重新分配等方面的官僚作风。当然这确实需要付出代价。 我认为很明显,说使用NSNumbers的NSArray会比简单迭代的C数组浮点数慢,所以我决定做一些测试,结果非常令人震惊! 我并没有想到它会变得这么糟糕。 注意:这些测试是在iPhone 3G上进行的,因为这是我感兴趣的目标平台。

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

我创建了一个简单的循环来总结每个数组的内容并使用mach_absolute_time()对它们进行计时。 NSMutableArray平均花费400倍! (不是400%,只是400倍,再延长40,000%!)。

标题:

// Array_Speed_TestViewController.h

//阵列速度测试

// 05/02/2009由Mehmet Akten创建。

//版权所有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

//阵列速度测试

// 05/02/2009由Mehmet Akten创建。

//版权所有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:枚举

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

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

////////////// From:darkdust.net

链接地址: http://www.djcxy.com/p/14369.html

上一篇: How do I iterate over an NSArray?

下一篇: .NET Standard vs .NET Core