如何迭代 NSArray? [英] How do I iterate over an NSArray?

查看:22
本文介绍了如何迭代 NSArray?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

10.5+/iOS 的普遍首选代码.

for(数组中的 id 对象){//对对象做一些事情}

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

还值得注意的是,虽然您在技术上可以使用 for-in 循环来单步执行 NSEnumerator,但我发现这几乎抵消了所有速度优势的快速枚举.原因是 -countByEnumeratorWithState: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) {//对对象做一些事情}];

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

<小时>

10.4 或更早版本

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

NSEnumerator *e = [array objectEnumerator];标识对象;while (object = [e nextObject]) {//对对象做一些事情}

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

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

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

<小时>

I'm looking for the standard idiom to iterate over an NSArray. My code needs to be suitable for OS X 10.4+.

解决方案

The generally-preferred code for 10.5+/iOS.

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

This construct is used to enumerate objects in a collection which conforms to the NSFastEnumeration protocol. This approach has a speed advantage because it stores pointers to several objects (obtained via a single method call) in a buffer and iterates through them by advancing through the buffer using pointer arithmetic. This is much faster than calling -objectAtIndex: each time through the loop.

It's also worth noting that while you technically can use a for-in loop to step through an NSEnumerator, I have found that this nullifies virtually all of the speed advantage of fast enumeration. The reason is that the default NSEnumerator implementation of -countByEnumeratingWithState:objects:count: places only one object in the buffer on each call.

I reported this in radar://6296108 (Fast enumeration of NSEnumerators is sluggish) but it was returned as Not To Be Fixed. The reason is that fast enumeration pre-fetches a group of objects, and if you want to enumerate only to a given point in the enumerator (e.g. until a particular object is found, or condition is met) and use the same enumerator after breaking out of the loop, it would often be the case that several objects would be skipped.

If you are coding for OS X 10.6 / iOS 4.0 and above, you also have the option of using block-based APIs to enumerate arrays and other collections:

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

You can also use -enumerateObjectsWithOptions:usingBlock: and pass NSEnumerationConcurrent and/or NSEnumerationReverse as the options argument.


10.4 or earlier

The standard idiom for pre-10.5 is to use an NSEnumerator and a while loop, like so:

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

I recommend keeping it simple. Tying yourself to an array type is inflexible, and the purported speed increase of using -objectAtIndex: is insignificant to the improvement with fast enumeration on 10.5+ anyway. (Fast enumeration actually uses pointer arithmetic on the underlying data structure, and removes most of the method call overhead.) Premature optimization is never a good idea — it results in messier code to solve a problem that isn't your bottleneck anyway.

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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆