循环遍历NSArray以获取对象

3
我目前正在开发一个应用程序,需要能够从NSArray中获取两个对象,然后将其存储在另一个对象中。
我目前正在进行快速枚举循环。
NSUInteger count = 0;
NSUInteger i = count + 1;
for (id item in [section items]) {


    item1 = [section.items objectAtIndex:count];
    item2 = [section.items objectAtIndex:i];

    count++;

}

现在,我想做的是获取第一个位置的对象并存储在item1中,然后第二个位置将存储在item2中。下一次循环时,我希望它将第三个位置中的对象存储在item1中,然后将第四个位置中的对象存储在item2中,依此类推。
有人试过并成功了吗?
编辑:
这是我目前拥有的代码,我认为最好我稍微解释一下我正在做什么。以下是我首先拥有的代码,之后我会解释。
MPSection *section = [self.sections objectAtIndex:indexPath.section];

NSArray *itemArray = [section items];
for (NSUInteger i = 0; (i + 1) < [section.items count]; i += 2) {
    item1 = [itemArray objectAtIndex:i];
    item2 = [itemArray objectAtIndex:i+1];
}

如您所见,这是在(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath中运行的,因为我想获取通常显示在UITableView的第一行和第二行中的内容,并将其放入一个分成两个子视图的单元格中。
我发现,使用上述代码肯定不会实现这一点。是否有更简单的方法可以做到这一点,如果有,可以请有人告诉我。我真的需要以最小的内存保留和时间消耗来处理这个问题。

“MPSection *section = [self.sections objectAtIndex:indexPath.row];” 这句话正确吗?还是你的意思是 “MPSection *section = [self.sections objectAtIndex:indexPath.section]”;? - Deepak Danduprolu
4个回答

3
最好能够预处理这个,但如果由于某些原因你不能这样做,那么你应该这样做。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    MPSection * section = [self.sections objectAtIndex:section];
    NSInteger   count   = [section.items count];

    return ((count % 2 == 0)? count / 2 : (count / 2 + 1) );
}

tableView:cellForRowAtIndexPath:方法中,
/* */

MPSection *section = [self.sections objectAtIndex:indexPath.section];

id item1 = [section.items objectAtIndex:(indexPath.row * 2)];
id item2 = ((indexPath.row * 2 + 1) < [section.items count])? [section.items objectAtIndex:(indexPath.row * 2 + 1)] : nil;

/* Use item1 & item2 to fill both the subviews */

原始答案

使用 NSEnumerator 实例来达到这个目的

NSEnumerator *enumerator = [section.items objectEnumerator];
id item1, item2;
while ( (item1 = [enumerator nextObject]) && (item2 = [enumerator nextObject]) ) {
    // Use item1 & item2
}

因此,我认为您在提到的代码片段中一定会遇到索引越界错误。

过度设计

对于性能存在一些疑问,因此我测试了三种建议的方法,并在循环中记录它们的时间。

Enumerator, Fast Enumeration with Object Search, For Loop (50000 elements): 19.253626, 88.269961, 18.767572
Enumerator, Fast Enumeration with Object Search, For Loop (25000 elements): 9.164311, 25.105664, 8.777443
Enumerator, Fast Enumeration with Object Search, For Loop (10000 elements): 3.428265, 6.035876, 3.144609
Enumerator, Fast Enumeration with Object Search, For Loop (5000 elements): 2.010748, 2.548562, 1.980477
Enumerator, Fast Enumeration with Object Search, For Loop (1000 elements): 0.508310, 0.389402, 0.338096
Enumerator, Fast Enumeration with Object Search, For Loop (500 elements): 0.156880, 0.163541, 0.150585
Enumerator, Fast Enumeration with Object Search, For Loop (100 elements): 0.076625, 0.034531, 0.036576
Enumerator, Fast Enumeration with Object Search, For Loop (50 elements): 0.026115, 0.022686, 0.041745

看起来,@Caleb的for循环方法可能是最好的方法。

1
他显然在使用NSFastEnumeration,它可以自动处理枚举。我觉得这有点过度杀伤力了。 - clstroud
1
为什么要过度设计呢?这个解决方案比快速枚举好多了,后者强制你逐个处理对象。这个解决方案一次处理两个对象,正是 OP 所需要的。 - Caleb
1
非常感谢您抽出时间进行测试!测试200,000、300,000和400,000个对象的情况可能会很有趣-- 显然,300,000是NSArray将为您带来惊喜的地方 - Caleb
顺便说一句,我最喜欢你的解决方案。它可能比我的传统for循环稍微慢一些,并且在功能上等同于我的枚举器解决方案,但将两个赋值都放在条件中是干净的,并直接表达了OP所表达的意图。 - Caleb
NSEnumerator方法在处理20万个项目时比其他两种方法更有效,但我怀疑OP没有处理那么多。那个链接很有价值。+1 - Deepak Danduprolu

3

如果您坚决要在此处使用快速枚举,可以设置一个标志,让您跳过循环的每个偶数迭代:

BOOL doThisOne = YES;
NSArray itemArray = [section itemArray];
for (id item in items) {
    if (doThisOne) {
        item1 = item;
        item2 = [itemArray objectAtIndex:1+[itemArray indexOfObject:item]];
    }
    doThisOne = !doThisOne;
}

注意: 如果数组中的项目数量为奇数,则上述代码会抛出范围异常。其他一些答案避免了这个问题,但我认为最好的答案是在此处不使用快速枚举。

使用一个枚举器或者一个普通的for循环将会更加简单:

NSEnumerator *e = [[section items] objectEnumerator];
while (item = [e nextObject]) {
    item1 = item;
    item2 = [e nextObject];
}

或者:

NSArray *itemArray = [section items];
for (int i = 0; (i + 1) < [items count]; i += 2) {
    item1 = [items objectAtIndex:i];
    item2 = [items objectAtIndex:i+1];
}

1
for(id item in array){
    if([array indexOfObject:item] % 2 != 0){
     item1 = item;
    }else{
     item2 = item;
    }
}

有趣的方法,Chris...但我不确定-[indexOfObject:]是否以O(1)运行。即使它确实如此,它是否比涉及for循环和本地索引变量的“显而易见”的答案更昂贵?-“匿名”批评家 - Coleman S
我不会说其中一个比另一个更“明显”,这只是我在脑海中首先解决问题的方式。这是一个奇偶问题,所以我认为这是最简单的方法。就性能而言,本地索引变量可能(很可能)成本较低,但我仍然坚持我的模数方法。 - clstroud
2
我认为问题不在于模数,而在于-indexOfObject:方法是否需要搜索数组以查找给定对象的索引。这可能是一个O(1)操作,也可能是O(n)。如果是后者,整个循环可能会变成O(n^2)而不是O(n),显然这是不可取的。 - Caleb
哦,我知道你在说什么。显然,越往下比较对象以获取索引,循环运行的时间就会越长。这就是为什么我说它可能会更快。如果它是一个包含约50个对象的数组,我认为差异应该可以忽略不计。但如果你想让代码适用于大型数组,你当然是正确的。 - clstroud
假设每个循环迭代执行需要1毫秒,如果n == 50,则O(n^2)解决方案需要运行2.5秒,而O(n)解决方案只需要50毫秒。 - Coleman S

1
int numItems = [section.items count];

for(int i = 0; i < count; i++) {
    item1 = [section.items objectAtIndex: i];
    item2 = ((i + 1) < count)) ? [section.items objectAtIndex: (i + 1)] : nil;
} 

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