如何迭代(遍历)NSArray?

15 浏览
0 Comments

如何迭代(遍历)NSArray?

我正在寻找迭代遍历NSArray的标准用法。我的代码需要适用于OS X 10.4+。

admin 更改状态以发布 2023年5月24日
0
0 Comments

对于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
}

0
0 Comments

广泛采用的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时,您可以很容易地更改为另一个可枚举集合(例如NSSetNSDictionary中的键等),或者甚至切换到-reverseObjectEnumerator以向后枚举数组,所有这些都不需要进行其他代码更改。如果枚举代码在方法中,则甚至可以传递任何NSEnumerator,代码甚至不必关心正在枚举的内容是什么。此外,NSEnumerator(至少由苹果代码提供的那些)会像存在更多对象一样保留它正在枚举的集合,因此您不必担心自动释放的对象存在多长时间。

也许NSEnumerator(或快速枚举)最大的好处是在枚举可变集合(数组或其他)时,您不会在不知情的情况下对其进行更改。如果通过索引访问对象,则可能会遇到奇怪的异常或偏差一错误(通常在问题发生后很长时间后)这可能非常难以调试。使用标准惯用法之一进行枚举具有“快速故障”行为,因此(由不正确的代码引起的)问题在发生变异后尝试访问下一个对象时会立即显现。随着程序变得更加复杂和多线程化,甚至依赖于第三方代码可能会修改的内容,脆弱的枚举代码变得越来越棘手。封装和抽象 FTW! 🙂


(此处为分割线)

0