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


当前回答

将每个方法添加到NSArray类别中,你会非常需要它

来自ObjectiveSugar的代码

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

其他回答

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

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

此构造用于枚举符合NSFastEnumeration协议的集合中的对象。这种方法具有速度优势,因为它将指向多个对象的指针(通过单个方法调用获得)存储在缓冲区中,并使用指针算术在缓冲区中遍历它们。这比每次在循环中调用-objectAtIndex要快得多。

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

我在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作为选项参数。


10.4或更早版本

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

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

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

When using -objectEnumerator, you very easily change to another enumerable collection (like an NSSet, keys in an NSDictionary, etc.), or even switch to -reverseObjectEnumerator to enumerate an array backwards, all with no other code changes. If the iteration code is in a method, you could even pass in any NSEnumerator and the code doesn't even have to care about what it's iterating. Further, an NSEnumerator (at least those provided by Apple code) retains the collection it's enumerating as long as there are more objects, so you don't have to worry about how long an autoreleased object will exist.

Perhaps the biggest thing an NSEnumerator (or fast enumeration) protects you from is having a mutable collection (array or otherwise) change underneath you without your knowledge while you're enumerating it. If you access the objects by index, you can run into strange exceptions or off-by-one errors (often long after the problem has occurred) that can be horrific to debug. Enumeration using one of the standard idioms has a "fail-fast" behavior, so the problem (caused by incorrect code) will manifest itself immediately when you try to access the next object after the mutation has occurred. As programs get more complex and multi-threaded, or even depend on something that third-party code may modify, fragile enumeration code becomes increasingly problematic. Encapsulation and abstraction FTW! :-)


这三种方法是:

        //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
    }];

三是最快的执行方式。有了自动补全功能,就不用写迭代信封了。

为迅速

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
}

将每个方法添加到NSArray类别中,你会非常需要它

来自ObjectiveSugar的代码

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

适用于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
}