在编程中经常需要列举collection(包含NSArrray,NSDictionary,NSSet等)中得元素,当前的Object-C语言有很多办法实现此功能,可以用标准的C语言循环,也可以用Object-C 1.0 的NSEnumerator以及Object-C 2.0的快速遍历(fast enumeration)。当语言中引入了“块”这一特性后。又多出几种新的遍历方式,而这几种方式容易被开发者忽视。采用这几种新方式遍历collection时,可以传入块,而collection中得每个元素都可能会放在块里面运行一遍,这种做法通常会大幅度的简化编码过程。如果了解过NSEnumerator的开发者可以直接从“基于块的遍历”看起。如果没有了解过的话,下面的例子会让你快速的了解NSEnumerator和块枚举。小编下面会详细介绍。
for循环
遍历数组的第一种方法就是采用老式的for循环,直接上码,更容易理解。
//for循环遍历
//NSArray
NSArray *anArray = [NSArray arrayWithObjects:@"林志玲",@"范冰冰",@"张馨予", nil];
for(int i = 0;i < anArray.count;i++)
{
id object = anArray[i];
// do something with "object"
NSLog(@"object_array:%@",object);
}
//Dictionary
NSDictionary *anDictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"林志玲",@"name",@"15810463139",@"number", nil];
NSArray *keys = [anDictionary allKeys];
for(int i = 0;i < keys.count;i++)
{
id key = keys[i];
id value = anDictionary[key];
// do something with "key" and "value"
NSLog(@"value:%@ key:%@",value,key);
}
//Set
NSSet *anSet = [NSSet setWithObjects:@"林志玲",@"范冰冰",@"张馨予",nil];
NSArray *objects = [anSet allObjects];
for(int i = 0;i < objects.count;i++)
{
id object = objects[i];
// do something with "object"
NSLog(@"object_set:%@",object);
}
使用Object-C 1.0 的NSEnumerator 来遍历
NSEnumerator是个抽象基类,其中只定义了两个方法,供其具体子类实现:
allobjects 和 nextObject。其中关键的方法是 nextObject,它返回枚举里面的下个对象。每次调用该方法时,数据结构都会更新。使得下次调用方法时返回下个对象。等到枚举中得所有对象都已经返回之后,再调用就将返回nil,这表示到枚举末端啦。
直接上码吧,更直白。。。。
//NSEnumerator遍历
//NSArray
NSArray *anArray = [NSArray arrayWithObjects:@"林志玲",@"范冰冰",@"张馨予", nil];
NSEnumerator *enumeratorArr = [anArray objectEnumerator];
id object;
while ((object = [enumeratorArr nextObject]) != nil) {
// do something with "object"
NSLog(@"object_array:%@",object);
}
//Dictionary
NSDictionary *anDictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"林志玲",@"name",@"15810463139",@"number", nil];
NSEnumerator *enumeratorDic = [anDictionary objectEnumerator];
id key;
while ((key = [enumeratorDic nextObject] )!= nil) {
id value = anDictionary[key];
// do something with "key" and "value"
NSLog(@"value:%@ key:%@",value,key);
}
//Set
NSSet *anSet = [NSSet setWithObjects:@"林志玲",@"范冰冰",@"张馨予",nil];
NSEnumerator *enumeratorSet = [anSet objectEnumerator];
id objectSet;
while ((objectSet = [enumeratorSet nextObject]) != nil) {
// do something with "object"
NSLog(@"object_set:%@",object);
}
总结:这种写法的功能与标准的for循环相似,但是代码却多了一些。真正的优势在于:不论遍历那种collection,都可以采用这套相似的语法。代码读起来更顺畅。
快速遍历
Object-C 2.0 引入了快速遍历这一功能,快速遍历与使用NSEnumerator 来遍历差不多,然而语法更简洁,它为for循环开设了 in 关键字。这个关键字大幅简化了遍历collection所需的语法。
代码更容易理解
//快速遍历
//NSArray
NSArray *anArray = [NSArray arrayWithObjects:@"林志玲",@"范冰冰",@"张馨予", nil];
for (id object in anArray) {
// do something with "object"
NSLog(@"object_array:%@",object);
}
//Dictionary
NSDictionary *anDictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"林志玲",@"name",@"15810463139",@"number", nil];
for (id key in anDictionary) {
id value = anDictionary[key];
// do something with "key" and "value"
NSLog(@"value:%@ key:%@",value,key);
}
//Set
NSSet *anSet = [NSSet setWithObjects:@"林志玲",@"范冰冰",@"张馨予",nil];
for (id object in anSet) {
// do something with "object"
NSLog(@"object_set:%@",object);
}
由于NSEnumerator 对象也实现啦NSFastEnumerator协议,所以能用来执行反向遍历。若要用反向遍历数组,可采用下面这种写法:
NSArray *aArray = [NSArray arrayWithObjects:@"林志玲",@"范冰冰",@"张馨予", nil];
for (id object in [aArray reverseObjectEnumerator]) {
// do something with "object"
NSLog(@"object_array:%@",object);
}
在目前介绍的遍历方式中,这种方法是语法最简单且效率最高的,然后如果在遍历字典时需要同时获取键与值,那么会多出来一步。缺点是无法获取当前遍历操作所针对的下标。
上面都是开胃菜,了解过NSEnumerator 的开发者可以直接跳到下面的“基于块的遍历方式”。
基于块的遍历方式
在当前的Object-C语言中,最新引入的一种做法就是基于块来遍历。NSArray中定义了下面这个方法,它可以实现最基本的遍历功能:
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
初次之外,还有一系列类似的遍历方法,它们可以接受各种选项,以便控制遍历操作。在遍历数组及集合时,每次迭代都要执行由block参数所传入的块, 这个块有三个参数,分别是当地前迭代针对的对象,所针对的下标,以及指向布尔值的指针。前两个参数的含义不言而喻。而通过第三个参数所提供的机制,开发者可以终止遍历操作。
先上一段代码消化消化:
NSArray *aArray = [NSArray arrayWithObjects:@"林志玲",@"范冰冰",@"张馨予", nil];
[aArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// do something with "object"
BOOL shouldStop;
NSLog(@"obj:%@ idx:%ld",obj,idx);
if ([obj isEqualToString:@"范冰冰"]) {
shouldStop = YES;
}
if (shouldStop) {
*stop = YES;
}
}];
这种写法稍微多了几行代码,不过依然明晰,而且遍历时既能获取对象,也能知道其下标。此方法还提供了一种很棒的机制,用来终止遍历操作,开发者可以通过stop变量来实现终止操作。当然,使用其他几种遍历方式,也可以通过break来实现终止循环。
此方法不仅可以用来遍历数组。NSSet里面也有同样地块枚举方法,NSDictionary也是这样,只是略有不同。
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block
要点
*遍历collection有四种方式。最基本的办法是for循环,其次是NSEnumerator遍历方法及快速遍历,最新,最先进的是“块枚举”法。
*“块枚举”法本身就通GCD来并发执行遍历操作,无须另行编写代码。而采用其他遍历方式无法轻易实现这一点。
*若提前知道待遍历的collection含有何种对象,则应该修改块签名,指出对象的具体类型。