在快速枚举中跟踪索引

29
我想在使用快速枚举时获取当前对象的索引,即
for (MyClass *entry in savedArray) {
// What is the index of |entry| in |savedArray|?
}
6个回答

65

查看NSArray的API,你会看到方法:

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block

那么试试这个吧。

[savedArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

    //... Do your usual stuff here

    obj  // This is the current object
    idx  // This is the index of the current object
    stop // Set this to true if you want to stop

}];

10
添加 - 停止标志通过调用 *stop = YES 来使用(仅使用 stop = YES 无效)。 - Tim

9
我想最直接的解决方法就是手动递增一个索引。
NSUInteger indexInSavedArray = 0;
for (MyClass *entry in savedArray) {
   indexInSavedArray++;
 }

或者,你可以选择不使用快速枚举。

    for (NSUInteger indexInSavedArray = 0; indexInSavedArray < savedArray.count; indexInSavedArray++) {
       [savedArray objectAtIndex:indexInSavedArray];
     }

5
他也可以使用[savedArray indexOfObject:entry];] :) - Roman Temchenko
6
是的,那样做是可行的,但非常低效,因为它必须将isEqual发送到与匹配对象之前的每个对象。例如,要获取第500个元素的索引,需要发送500个isEqual消息(如果对象在isEqual中实际执行操作,则还需要计算),然后再发送501个消息来获取该对象之后的下一个对象。 编辑:也许你是在挖苦我,我错过了一个笑话 :) - Josh Rosen

7

这个问题已经得到了回答,但我想补充一点的是计数迭代实际上是iOS开发文库文档中提到的技术:

NSArray *array = <#Get an array#>;
NSUInteger index = 0;

for (id element in array) {
    NSLog(@"Element at index %u is: %@", index, element);
    index++;
}

我确信会有一个高端的技巧,但我猜没有。 :)

1
苹果的方式是不安全的,我认为应该避免使用 - 当有人无意中向您的代码添加“break”或“continue”时,它会出现严重错误。可悲的是,他们为快速枚举设计的方式并不是很好。 - Adam
如果在快速枚举代码中放置“break”或“continue”,是否意味着出现了问题?苹果是否与C#有所不同?只是为了自己的学习(和避免未来的错误)而问。谢谢! - GrandAdmiral
3
简单例子:这个示例代码只有2行。如果它有20行,在第5行,有人加了一个"continue",这个"continue"会导致"element"继续执行,但是"index"不会。哎呀!所以你把index++放在使用它的地方之后开始。但是如果稍后尝试在循环体中再次引用它怎么办?现在它比实际要大1。...等等。 - Adam
2
@Adam 虽然我同意你的观点,即快速枚举设计得不太好,但是你可以使用 NSUInteger index = -1; 然后在开始时递增索引吗? - Jimmery
@jimmery,没错,那样做没问题。但是看起来不对-您正在创建一个错误值的索引,并且我已经看到过许多复制/粘贴代码或重构代码的情况,在这种情况下,“-1”的重要性被忽略了。基本上:当您需要计数器时,请勿使用快速枚举(它很危险,而且它只为您节省了一行代码)。 - Adam
NSUInteger 也是一个无符号整数,所以这行代码最终会成为一个巨大的数字。 - Sam Clewlow

3
如果您想在此处访问索引或返回,则以下代码可能会有用(假设数组是NSString类型的数组)。
- (NSInteger) findElemenent:(NSString *)key inArray:(NSArray *)array
{
    __block NSInteger index = -1;
    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isEqualToString:key]) {
            *stop = YES;
            index = idx;
        }
    }];
    return index;
}

0

我刚刚遇到了一个相当严重的错误,因为我按照这里所有人建议的方式去做。也就是说,“创建一个索引变量,并在循环结束时将其递增”。

我建议应该避免这种方式,而应该遵循以下模式:

int index = -1;
for (a in b) {
   index++;
   //Do stuff with `a`
}

我推荐这种奇怪的模式的原因是,如果你使用快速枚举的continue;功能,它将跳过你循环结束时的最后一行index++代码,导致你的索引计数错误!因此,我建议从-1开始递增,然后再执行其他操作。
至于那些说只需使用indexOfObject:的人,这在存在重复条目时是行不通的。

0
一个简单的观察:如果你将索引初始化为-1,然后将++index放在for循环的第一行,那么这不就覆盖了所有情况吗(只要没有人在增量前面插入代码)?

1
只要没有人在递增操作前插入代码,这就是最好的基于信仰的编码方式。 - Tim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接