如何迭代(遍历)NSArray?
广泛采用的10.5+ / iOS代码。
for (id object in array) { // do something with object }
此结构用于枚举符合 NSFastEnumeration
协议的集合中的对象。这种方法具有速度优势,因为它通过在缓冲区中存储多个对象的指针(通过单个方法调用获得)并通过指针算术推进缓冲区来迭代它们。这比每次通过循环调用 -objectAtIndex:
要快得多。
值得一提的是,虽然技术上可以使用 for-in
循环遍历 NSEnumerator
,但我发现这几乎抵消了快速枚举的所有速度优势。原因是默认的 NSEnumerator
实现 -countByEnumeratingWithState:objects:count:
在每次调用时只将一个对象放入缓冲区。
我在 radar:// 6296108
(Fast enumeration of NSEnumerators is sluggish) 中报告了这个问题,但被退回为无需修复。原因是快速枚举预取一组对象,如果您想枚举到一定点(例如直到找到特定的对象或满足条件)并在退出循环后继续使用相同的枚举器,则通常会跳过几个对象。
如果你正在编写 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+上使用快速枚举将获得更大的改进。 (快速枚举实际上在基础数据结构上使用指针运算,并消除了大部分方法调用开销。)过早地进行优化从来不是一个好主意-它会导致更混乱的代码来解决不是瓶颈的问题。
当使用-objectEnumerator
时,您可以很容易地更改为另一个可枚举集合(例如NSSet
,NSDictionary
中的键等),或者甚至切换到-reverseObjectEnumerator
以向后枚举数组,所有这些都不需要进行其他代码更改。如果枚举代码在方法中,则甚至可以传递任何NSEnumerator
,代码甚至不必关心正在枚举的内容是什么。此外,NSEnumerator
(至少由苹果代码提供的那些)会像存在更多对象一样保留它正在枚举的集合,因此您不必担心自动释放的对象存在多长时间。
也许NSEnumerator
(或快速枚举)最大的好处是在枚举可变集合(数组或其他)时,您不会在不知情的情况下对其进行更改。如果通过索引访问对象,则可能会遇到奇怪的异常或偏差一错误(通常在问题发生后很长时间后)这可能非常难以调试。使用标准惯用法之一进行枚举具有“快速故障”行为,因此(由不正确的代码引起的)问题在发生变异后尝试访问下一个对象时会立即显现。随着程序变得更加复杂和多线程化,甚至依赖于第三方代码可能会修改的内容,脆弱的枚举代码变得越来越棘手。封装和抽象 FTW! 🙂
(此处为分割线)