我如何迭代NSArray?

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

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

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

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

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

我在radar://6296108报告这个radar://6296108radar://6296108快速枚举很慢),但是它被返回为不被修正。 原因是快速枚举预取一组对象,并且如果只想枚举到枚举数中的给定点(例如,直到find特定对象,或满足条件)并且在分解之后使用相同的枚举器的循环,往往会是几个对象会被跳过的情况。

如果您正在编写OS X 10.6 / iOS 4.0及更高版本,则还可以select使用基于块的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 } 

我build议保持简单。 将自己-objectAtIndex:到一个数组types是不灵活的,而使用-objectAtIndex:的声称速度增加对于-objectAtIndex:上的快速枚举的改进是微不足道的。 (快速枚举实际上是在底层数据结构上使用指针运算,并且消除了大部分的方法调用开销。)过早优化从来就不是一个好主意 – 它会导致更复杂的代码来解决一个不是瓶颈的问题。

当使用-objectEnumerator ,你可以很容易地转换到另一个可枚举的集合(比如NSSetNSDictionary键等等),甚至可以切换到-reverseObjectEnumerator来向后枚举一个数组,所有的代码都没有改变。 如果迭代代码是在一个方法中,你甚至可以传入任何NSEnumerator ,代码甚至不必关心它的迭代。 此外,只要有更多的对象,一个NSEnumerator (至less那些由Apple代码提供的)保留了它枚举的集合,所以你不必担心自动释放对象存在多久。

也许NSEnumerator (或快速枚举)保护你的最大的事情是在你枚举它的时候,在你的底下有一个可变的集合(数组或其他)。 如果通过索引访问对象,则可能会遇到奇怪的exception或错误(通常在问题发生后很长时间),这些错误对于debugging来说是非常可怕的。 枚举使用其中一种标准惯用语具有“快速失败”的行为,所以当您尝试在发生变化之后访问下一个对象时,问题(由不正确的代码引起)将立即显现。 随着程序变得更加复杂和multithreading,甚至依赖于第三方代码可能修改的内容,脆弱的枚举代码变得越来越成问题。 封装和抽象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 } 

testing结果和源代码如下(可以设置应用程序的迭代次数)。 时间以毫秒为单位,每个条目是运行testing5-10次的平均结果。 我发现一般情况下它是2-3位有效数字,之后每次运行都会有所不同。 这给出了一个小于1%的误差范围。 testing在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等)为pipe理信息提供了一个非常好的界面,而不必担心内存pipe理,重新分配等方面的官僚作风。 。 我认为这很明显,说使用NSNumbers的NSArray将比C数组的浮点数更简单的迭代,所以我决定做一些testing,结果是非常令人震惊! 我并不期待它会这样糟糕。 注意:这些testing是在iPhone 3G上进行的,因为这是我感兴趣的目标平台。

在这个testing中,我在NSNumbers的C float *和NSArray之间做了一个非常简单的随机访问性能比较

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

标题:

// Array_Speed_TestViewController.h

//arrays速度testing

//在05/02/2009由Mehmet Akten创build

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

//arrays速度testing

//在05/02/2009由Mehmet Akten创build

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

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

自引入块以来可用,这允许迭代具有块的数组。 它的语法不如枚举快,但有一个非常有趣的function:并发枚举。 如果枚举顺序不重要,并且可以在不locking的情况下并行完成作业,则可以在多核系统上提供相当大的加速。 更多关于在并发枚举部分。

 [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

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

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

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

objectAtIndex:枚举

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

 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.自动完成忘记写迭代信封。

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

来自ObjectiveSugar的代码

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

这里是你如何声明一个string数组并迭代它们:

 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 }