objective-c 如何迭代 NSArray?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/992901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I iterate over an NSArray?
提问by Steve McLeod
I'm looking for the standard idiom to iterate over an NSArray. My code needs to be suitable for OS X 10.4+.
我正在寻找迭代 NSArray 的标准习惯用法。我的代码需要适用于 OS X 10.4+。
回答by Quinn Taylor
The generally-preferred code for 10.5+/iOS.
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 NSFastEnumerationprotocol. 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 muchfaster than calling -objectAtIndex:each time through the loop.
此构造用于枚举符合NSFastEnumeration协议的集合中的对象。这种方法具有速度优势,因为它将指向多个对象(通过单个方法调用获得)的指针存储在缓冲区中,并通过使用指针算法在缓冲区中前进来迭代它们。这是很多比调用速度-objectAtIndex:通过每一次循环中。
It's also worth noting that while you technically canuse 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 NSEnumeratorimplementation of -countByEnumeratingWithState:objects:count:places only one object in the buffer on each call.
还值得注意的是,虽然您在技术上可以使用 for-in 循环来单步执行NSEnumerator,但我发现这几乎抵消了快速枚举的所有速度优势。原因是每次调用的默认NSEnumerator实现-countByEnumeratingWithState:objects:count:只在缓冲区中放置一个对象。
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.
我在radar://6296108(NSEnumerators 的快速枚举是缓慢的)中报告了这一点,但它返回为 Not To Be Fixed。原因是快速枚举预取一组对象,并且如果您只想枚举到枚举器中的给定点(例如,直到找到特定对象或满足条件)并在爆发后使用相同的枚举器在循环中,经常会出现多个对象被跳过的情况。
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:
如果您正在为 OS X 10.6 / iOS 4.0 及更高版本编码,您还可以选择使用基于块的 API 来枚举数组和其他集合:
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
You can also use -enumerateObjectsWithOptions:usingBlock:and pass NSEnumerationConcurrentand/or NSEnumerationReverseas the options argument.
您还可以使用-enumerateObjectsWithOptions:usingBlock:和传递NSEnumerationConcurrent和/或NSEnumerationReverse作为选项参数。
10.4 or earlier
10.4 或更早版本
The standard idiom for pre-10.5 is to use an NSEnumeratorand a while loop, like so:
10.5 之前的标准习惯用法是使用一个NSEnumerator和一个 while 循环,如下所示:
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.
我建议保持简单。将自己绑定到数组类型是不灵活的,而且据称使用的速度增加-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 -reverseObjectEnumeratorto enumerate an array backwards, all with no other code changes. If the iteration code is in a method, you could even pass in any NSEnumeratorand the code doesn't even have to care about whatit'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.
使用 时-objectEnumerator,您可以轻松更改为另一个可枚举集合(如 an NSSet、an中的键NSDictionary等),甚至切换到-reverseObjectEnumerator向后枚举数组,所有代码均无需更改。如果迭代代码在一个方法中,你甚至可以传入 anyNSEnumerator并且代码甚至不必关心它在迭代什么。此外,NSEnumerator只要有更多对象,一个(至少由 Apple 代码提供的那些)就会保留它正在枚举的集合,因此您不必担心自动释放的对象将存在多长时间。
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 knowledgewhile 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! :-)
也许NSEnumerator(或快速枚举)保护您免受的最大事情是在您枚举它时在您不知情的情况下在您下面更改可变集合(数组或其他)。如果您按索引访问对象,您可能会遇到奇怪的异常或一对一错误(通常在问题发生很久之后),这对于调试来说可能是可怕的。使用标准习语之一的枚举具有“快速失败”行为,因此当您在发生突变后尝试访问下一个对象时,问题(由不正确的代码引起)将立即显现出来。随着程序变得越来越复杂和多线程,甚至依赖于第三方代码可能会修改的东西,脆弱的枚举代码变得越来越成问题。封装和抽象 FTW!:-)
回答by diederikh
For OS X 10.4.x and previous:
对于 OS X 10.4.x 及更早版本:
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
For OS X 10.5.x (or iPhone) and beyond:
对于 OS X 10.5.x(或 iPhone)及更高版本:
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
回答by Hitendra Solanki
The results of the test and source code are below (you can set the number of iterations in the app). The time is in milliseconds, and each entry is an average result of running the test 5-10 times. I found that generally it is accurate to 2-3 significant digits and after that it would vary with each run. That gives a margin of error of less than 1%. The test was running on an iPhone 3G as that's the target platform I was interested in.
测试结果和源码如下(可以在app中设置迭代次数)。时间以毫秒为单位,每个条目是运行测试5-10次的平均结果。我发现通常它可以精确到 2-3 位有效数字,之后每次运行都会有所不同。这给出了小于 1% 的误差幅度。该测试在 iPhone 3G 上运行,因为这是我感兴趣的目标平台。
numberOfItems NSArray (ms) C Array (ms) Ratio
100 0.39 0.0025 156
191 0.61 0.0028 218
3,256 12.5 0.026 481
4,789 16 0.037 432
6,794 21 0.050 420
10,919 36 0.081 444
19,731 64 0.15 427
22,030 75 0.162 463
32,758 109 0.24 454
77,969 258 0.57 453
100,000 390 0.73 534
The classes provided by Cocoa for handling data sets (NSDictionary, NSArray, NSSet etc.) provide a very nice interface for managing information, without having to worry about the bureaucracy of memory management, reallocation etc. Of course this does come at a cost though. I think it's pretty obvious that say using an NSArray of NSNumbers is going to be slower than a C Array of floats for simple iterations, so I decided to do some tests, and the results were pretty shocking! I wasn't expecting it to be this bad. Note: these tests are conducted on an iPhone 3G as that's the target platform I was interested in.
Cocoa 提供的用于处理数据集的类(NSDictionary、NSArray、NSSet 等)为管理信息提供了一个非常好的界面,而不必担心内存管理、重新分配等官僚主义。当然,这确实是有代价的. 我认为很明显,对于简单的迭代,使用 NSNumbers 的 NSArray 将比浮点数的 C 数组慢,所以我决定做一些测试,结果非常令人震惊!我没想到会这么糟糕。注意:这些测试是在 iPhone 3G 上进行的,因为这是我感兴趣的目标平台。
In this test I do a very simple random access performance comparison between a C float* and NSArray of NSNumbers
在这个测试中,我在一个 C float* 和 NSNumbers 的 NSArray 之间做了一个非常简单的随机访问性能比较
I create a simple loop to sum up the contents of each array and time them using mach_absolute_time(). The NSMutableArray takes on average 400 times longer!! (not 400 percent, just 400 times longer! thats 40,000% longer!).
我创建了一个简单的循环来总结每个数组的内容并使用 mach_absolute_time() 对它们计时。NSMutableArray 花费的时间平均要长 400 倍!!(不是 400%,只是长了 400 倍!那是长了 40,000%!)。
Header:
标题:
// Array_Speed_TestViewController.h
// Array_Speed_TestViewController.h
// Array Speed Test
// 阵列速度测试
// Created by Mehmet Akten on 05/02/2009.
// 由 Mehmet Akten 于 2009 年 5 月 2 日创建。
// Copyright MSA Visuals Ltd. 2009. All rights reserved.
// 版权所有 MSA Visuals Ltd. 2009。保留所有权利。
#import <UIKit/UIKit.h>
@interface Array_Speed_TestViewController : UIViewController {
int numberOfItems; // number of items in array
float *cArray; // normal c array
NSMutableArray *nsArray; // ns array
double machTimerMillisMult; // multiplier to convert mach_absolute_time() to milliseconds
IBOutlet UISlider *sliderCount;
IBOutlet UILabel *labelCount;
IBOutlet UILabel *labelResults;
}
-(IBAction) doNSArray:(id)sender;
-(IBAction) doCArray:(id)sender;
-(IBAction) sliderChanged:(id)sender;
@end
Implementation:
执行:
// Array_Speed_TestViewController.m
// Array_Speed_TestViewController.m
// Array Speed Test
// 阵列速度测试
// Created by Mehmet Akten on 05/02/2009.
// 由 Mehmet Akten 于 2009 年 5 月 2 日创建。
// Copyright MSA Visuals Ltd. 2009. All rights reserved.
// 版权所有 MSA Visuals Ltd. 2009。保留所有权利。
#import "Array_Speed_TestViewController.h"
#include <mach/mach.h>
#include <mach/mach_time.h>
@implementation Array_Speed_TestViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(@"viewDidLoad");
[super viewDidLoad];
cArray = NULL;
nsArray = NULL;
// read initial slider value setup accordingly
[self sliderChanged:sliderCount];
// get mach timer unit size and calculater millisecond factor
mach_timebase_info_data_t info;
mach_timebase_info(&info);
machTimerMillisMult = (double)info.numer / ((double)info.denom * 1000000.0);
NSLog(@"machTimerMillisMult = %f", machTimerMillisMult);
}
// pass in results of mach_absolute_time()
// this converts to milliseconds and outputs to the label
-(void)displayResult:(uint64_t)duration {
double millis = duration * machTimerMillisMult;
NSLog(@"displayResult: %f milliseconds", millis);
NSString *str = [[NSString alloc] initWithFormat:@"%f milliseconds", millis];
[labelResults setText:str];
[str release];
}
// process using NSArray
-(IBAction) doNSArray:(id)sender {
NSLog(@"doNSArray: %@", sender);
uint64_t startTime = mach_absolute_time();
float total = 0;
for(int i=0; i<numberOfItems; i++) {
total += [[nsArray objectAtIndex:i] floatValue];
}
[self displayResult:mach_absolute_time() - startTime];
}
// process using C Array
-(IBAction) doCArray:(id)sender {
NSLog(@"doCArray: %@", sender);
uint64_t start = mach_absolute_time();
float total = 0;
for(int i=0; i<numberOfItems; i++) {
total += cArray[i];
}
[self displayResult:mach_absolute_time() - start];
}
// allocate NSArray and C Array
-(void) allocateArrays {
NSLog(@"allocateArrays");
// allocate c array
if(cArray) delete cArray;
cArray = new float[numberOfItems];
// allocate NSArray
[nsArray release];
nsArray = [[NSMutableArray alloc] initWithCapacity:numberOfItems];
// fill with random values
for(int i=0; i<numberOfItems; i++) {
// add number to c array
cArray[i] = random() * 1.0f/(RAND_MAX+1);
// add number to NSArray
NSNumber *number = [[NSNumber alloc] initWithFloat:cArray[i]];
[nsArray addObject:number];
[number release];
}
}
// callback for when slider is changed
-(IBAction) sliderChanged:(id)sender {
numberOfItems = sliderCount.value;
NSLog(@"sliderChanged: %@, %i", sender, numberOfItems);
NSString *str = [[NSString alloc] initWithFormat:@"%i items", numberOfItems];
[labelCount setText:str];
[str release];
[self allocateArrays];
}
//cleanup
- (void)dealloc {
[nsArray release];
if(cArray) delete cArray;
[super dealloc];
}
@end
From : memo.tv
来自 : memo.tv
////////////////////
/////////////////////
Available since the introduction of blocks, this allows to iterate an array with blocks. Its syntax isn't as nice as fast enumeration, but there is one very interesting feature: concurrent enumeration. If enumeration order is not important and the jobs can be done in parallel without locking, this can provide a considerable speedup on a multi-core system. More about that in the concurrent enumeration section.
自从引入块以来就可用,这允许用块迭代数组。它的语法不如快速枚举好,但有一个非常有趣的特性:并发枚举。如果枚举顺序不重要并且可以在没有锁定的情况下并行完成作业,则这可以在多核系统上提供相当大的加速。更多关于并发枚举部分的内容。
[myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
[self doSomethingWith:object];
}];
[myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomethingWith:object];
}];
/////////// NSFastEnumerator
/////////// NSFastEnumerator
The idea behind fast enumeration is to use fast C array access to optimize iteration. Not only is it supposed to be faster than traditional NSEnumerator, but Objective-C 2.0 also provides a very concise syntax.
快速枚举背后的想法是使用快速 C 数组访问来优化迭代。它不仅应该比传统的 NSEnumerator 更快,而且 Objective-C 2.0 还提供了非常简洁的语法。
id object;
for (object in myArray) {
[self doSomethingWith:object];
}
/////////////////
/////////////////
NSEnumerator
NSE分子
This is a form of external iteration: [myArray objectEnumerator] returns an object. This object has a method nextObject that we can call in a loop until it returns nil
这是一种外部迭代形式:[myArray objectEnumerator] 返回一个对象。这个对象有一个 nextObject 方法,我们可以在循环中调用它,直到它返回 nil
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
/////////////////
objectAtIndex: enumeration
objectAtIndex:枚举
Using a for loop which increases an integer and querying the object using [myArray objectAtIndex:index] is the most basic form of enumeration.
使用增加整数的 for 循环并使用 [myArray objectAtIndex:index] 查询对象是最基本的枚举形式。
NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
[self doSomethingWith:[myArray objectAtIndex:index]];
}
////////////// From : darkdust.net
///////////// 来自:darkdust.net
回答by Javier Calatrava Llavería
The three ways are:
这三种方式是:
//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
}];
- Is the fastest way in execution, and 3. with autocompletion forget about writing iteration envelope.
- 是执行中最快的方式,并且 3. 用自动完成忘记写迭代信封。
回答by onmyway133
Add eachmethod in your NSArray category, you gonna need it a lot
each在你的 中添加方法NSArray category,你会非常需要它
Code taken from ObjectiveSugar
代码取自ObjectiveSugar
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
回答by oabarca
Here is how you declare an array of strings and iterate over them:
以下是声明字符串数组并对其进行迭代的方法:
NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];
for (int i = 0; i < [langs count]; i++) {
NSString *lang = (NSString*) [langs objectAtIndex:i];
NSLog(@"%@, ",lang);
}
回答by Nilesh R Patel
For Swift
对于斯威夫特
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
}
回答by Nilesh R Patel
Do this :-
做这个 :-
for (id object in array)
{
// statement
}

