如何遍历NSArray?


Answers:


667

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作为options参数。


10.4或更早版本

10.5之前版本的标准习惯用法是使用NSEnumeratorwhile循环,如下所示:

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(或快速枚举)保护您免受的最大伤害可能是在枚举时在您不知情的情况下对您下面的可变集合(数组或其他)进行了更改。如果按索引访问对象,则可能会遇到奇怪的异常或异常错误(通常是在问题发生后很长时间),这很容易调试。使用标准习语之一进行枚举具有“快速失败”的行为,因此,当您在发生突变后尝试访问下一个对象时,该问题(由错误的代码引起)将立即显现出来。随着程序变得更加复杂和多线程,或者甚至依赖于第三方代码可能会修改的内容,脆弱的枚举代码变得越来越有问题。封装和抽象FTW!:-)



28
注意:大多数编译器都会发出有关“ while(object = [e nextObject])”的警告。在这种情况下,您实际上确实打算使用=而不是==。要取消显示警告,可以添加额外的括号:“ while(((object = [e nextObject])))”。
亚当·罗森菲尔德2009年

与NSEnumerator一起记住的另一件事是,它将比objectWithIndex方法使用更多的内存(作为数组的副本)。
deaderikh

@QuinnTaylor使用for (id object in array),是否还可以确定对象在数组中的当前索引,还是需要包含一个单独的计数器?
Coderama 2012年

1
您需要一个单独的计数器,但是我建议您考虑使用基于块的枚举(确实包含索引)。
奎因·泰勒

我很奇怪,但是我使用了for这样的循环:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
nielsbot

125

对于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
在第一个示例中,您甚至不必在循环外声明int。这也可以正常工作,并且很好地调整了变量的范围,因此您以后可以根据需要重用它:for(int i = 0; i <[myArray count]; i ++)...但是,请注意,每次都调用-count通过数组可以抵消使用-objectAtIndex的好处:
Quinn Taylor

1
实际上,如果您要枚举可变集合,则从技术上讲,每次循环检查计数都更为正确。我澄清了我的答案,以解释使用NSEnumerator或NSFastEnumeration可以防止阵列的并发突变。
奎因·泰勒

for(int i = 0; ...)结构是C语言方言(相信会使用C99),但我不确定使用的是XCode默认语言。
deaderikh

C99是Xcode 3.1.x的默认设置-在将来的某个时候,默认值将更改为GNU99,GNU99(除其他功能外)还支持匿名联合和结构。那应该很好...
Quinn Taylor

2
使用for (NSUInteger i = 0, count = [myArray count]; i < count; i++)这种方法可能是最有效,最简洁的方法。
奎因·泰勒

17

测试结果和源代码如下(您可以在应用程序中设置迭代次数)。时间以毫秒为单位,每个条目都是运行测试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上进行的,因为这是我感兴趣的目标平台。

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

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

标头:

// Array_Speed_TestViewController.h

//阵列速度测试

//由Mehmet Akten创建于2009年5月2日。

//版权所有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创建于2009年5月2日。

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


11

三种方式是:

        //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.具有自动补全功能,因此无需编写迭代包络。

7

each在您的方法中添加方法NSArray category,您将非常需要它

代码取自ObjectiveSugar

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

0

对于斯威夫特

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
}

-1

做这个 :-

for (id object in array) 
{
        // statement
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.