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

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

问题描述

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

解决方案

10.5 + p>

  for(数组中的id对象){
//与对象执行操作
}

此构造用于枚举集合中符合 [NSFastEnumeration protocol] code>( Cocoa参考)。这种方法具有速度优势,因为它将指向几个对象(通过单个方法调用获得)的指针存储在缓冲器中,并通过使用指针算术前进通过缓冲器来遍历它们。这是比每次通过循环调用 -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){
//使用对象
执行某项操作}];

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


$ b b


10.4或更早



10.5之前的标准习语是使用 NSEnumerator 和一个while循环,如下:

  NSEnumerator * e = [array objectEnumerator]; 
id object;
while(object = [e nextObject]){
//使用对象
执行操作}

我建议保持简单。将自己绑定到数组类型是不灵活的,并且使用 -objectAtIndex:的声称的速度增加对于快速枚举在10.5+上的改进是无关紧要的。 (快速枚举实际上对底层数据结构使用指针算法,并且消除了大部分的方法调用开销。)过早优化从来不是一个好主意 - 它导致了一个更麻烦的代码来解决一个不是瓶颈的问题。 p>

使用 -objectEnumerator 时,您很容易更改为另一个可枚举集合code> NSSet ,键在 NSDictionary 等),甚至切换到 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](Cocoa Reference). 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天全站免登陆