Objective-c:集合类型遍历方法详解

来源:互联网 发布:金星网络商学院 编辑:程序博客网 时间:2024/06/06 03:02

前言

在编程中经常需要遍历集合类型的元素,当前有多种方法实现此功能,初学者大多习惯于用标准的C语言循环,当然也可以使用 NSEnumerator 以及快速遍历(for-in),Objective-c 引入‘块’特性后,又多出来几种新的遍历方式,采用这几种新的遍历方式,可大幅度简化代码过程。

本文主要讲解常用的几种集合类型:NSArrayNSSet 以及NSDictionary 的遍历方式。

for 循环遍历

  • 遍历数组(NSArray)
// 初始化数组NSArray *numbers = @[@1, @2, @3, @4, @5];// 遍历数组元素for (int i = 0; i < numbers.count; i++) {    id obj = numbers[i];    NSLog(@"%@", obj);}
  • 遍历集(NSSet)
// 初始化集NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];// 获取集元素NSArray *objects = [sets allObjects];// 遍历集元素for (int i = 0; i < objects.count; i++) {    id obj = objects[i];    NSLog(@"%@", obj);}
  • 遍历字典(NSDictionary)
// 初始化字典NSDictionary *infomation = @{@"account" : @"admin",                             @"password": @123456,                             @"members" : @YES,                             @"state"   : @"activation"};// 获取字典键NSArray *keys = [dicts.allKeys mutableCopy];// 遍历字典键值对for (int i = 0; i < keys.count; i++) {    id key = keys[i];    id obj = dicts[key];    NSLog(@"%@", obj);}

Tips

1、字典与set都是“无序”的,所以无法根据特定的整数下标来直接访问其中的值,于是,在遍历的时候需要获取字典的所有键或是set里的所有对象,此过程将会造成额外开销。

2、for循环也可以实现方向遍历,计数器的值从“元素个数减1”开始,每次迭代时递减,直到0为止。执行方向遍历时,使用for循环会比其他方式简单很多。

NSEnumerator 遍历

  • NSEnumerator提供的nextObject方法,返回枚举里的下个对象。每次调用该方法时,其内部数据结构都会更新,使得下次调用方法时能返回下个对象。等到枚举中的全部对象都已返回之后,再调用就将返回nil,这表示达到枚举末端了。

  • 遍历数组(NSArray)

// 初始化数组NSArray *numbers = @[@1, @2, @3, @4, @5];// 创建枚举器NSEnumerator *enumerator = [numbers objectEnumerator];// 创建迭代元素id obj = nil;// 遍历数组元素while ((obj = [enumerator nextObject]) != nil) {    NSLog(@"%@", obj);}
  • 遍历集(NSSet)
// 初始化集NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];// 获取集元素NSArray *objects = [sets allObjects];id obj = nil;// 遍历集元素while ((obj = [enumerator nextObject]) != nil) {    NSLog(@"%@", obj);}
  • 遍历字典(NSDictionary)
// 初始化字典NSDictionary *infomation = @{@"account" : @"admin",                             @"password": @123456,                             @"members" : @YES,                             @"state"   : @"activation"};NSEnumerator *enumerator = [infomation keyEnumerator];id key = nil;// 遍历字典键值对while ((key = [enumerator nextObject]) != nil) {    id obj = dicts[key];    NSLog(@"%@ : %@", key, obj);}
  • 遍历字典的方式与数组和set略有不同,因为字典里既有键也有值,所以要根据给定的键把对应的值提取出来。使用NSEnumerator还有个好处,就是有多种枚举器可供使用。比如,有反向遍历数组的枚举器,如果拿它来遍历,就可以按反方向来迭代集合中的元素了。
NSArray *numbers = @[@1, @2, @3, @4, @5];NSEnumerator *enumerator = [numbers reverseObjectEnumerator];id obj = nil;while ((obj = [enumerator nextObject]) != nil) {    NSLog(@"%@", obj);}// 输出:5 4 3 2 1

快速遍历

  • 快速遍历与使用NSEnumerator来遍历差不多,然而语法更简洁,它为for循环开设了in关键字。这个关键字大幅简化了遍历集合所需的语法。

  • 遍历数组(NSArray)

NSArray *numbers = @[@1, @2, @3, @4, @5];for (id obj in numbers) {    NSLog(@"%@", obj);}
  • 遍历集(NSSet)
NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];for (id obj in sets) {    NSLog(@"%@", obj);}
  • 遍历字典(NSDictionary)
NSDictionary *infomation = @{@"account" : @"admin",                             @"password": @123456,                             @"members" : @YES,                             @"state"   : @"activation"};for (id key in dicts.allKeys) {    id obj = dicts[key];    NSLog(@"%@ : %@", key, obj);}
  • 由于NSEnumerator对象也实现了NSFastEnumeration协议,所以能用来执行方向遍历。若要反向遍历数组,可采用下面这种写法:
NSArray *numbers = @[@1, @2, @3, @4, @5];for (id obj in [numbers reverseObjectEnumerator]) {    NSLog(@"%@", obj);}// 输出:5 4 3 2 1

基于块的遍历方式

  • 在遍历数组及set时,每次迭代都要执行由block参数所传入的块,这个块有三个参数,分别是当前迭代所针对的对象、所针对的下标、以及指向布尔值的指针。前两个参数的含义不言而喻。而通过第三个参数所提供的机制,开发者可以终止遍历操作。

  • 遍历数组(NSArray)

NSArray *numbers = @[@1, @2, @3, @4, @5];[numbers enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {    NSLog(@"%@", obj);    if (idx == 3) {        *stop = YES;    }}];
  • 遍历集(NSSet)
NSSet *sets = [NSSet setWithObjects:@1, @2, @3, @4, @5, nil];[sets enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {    NSLog(@"%@", obj);    if(shouldStop) {       *stop = YES;    }}];
  • 遍历字典(NSDictionary)
NSDictionary *infomation = @{@"account" : @"admin",                             @"password": @123456,                             @"members" : @YES,                             @"state"   : @"activation"};[infomation enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {    NSLog(@"%@:%@", key, obj);    if(shouldStop) {       *stop = YES;    }}];
  • 通过基于块的遍历方式可以直接从块里获取更多信息。在遍历数组时,可以知道当前所针对的下标。遍历有序set时也一样。而在遍历字典时,无需额外编码,即可同时获取键与值,因而省去了根据给定键来获取对应值这一步。用这种方式遍历字典,可以同时得知键与值,这很可能比其他方式快很多,因为在字典内部的数据结构中,键与值本来就是存储在一起的。

  • 另外一个好处是,能够修改块的方法前面,以免进行类型转换操作,从效果上讲,相当于把本来需要执行的类型转换操作交给块方法签名来做。比方说,要用“快速遍历法”来遍历字典。若已知字典中的对象必须为字符串,则可以这样编码:

for (NSString *key in aDictionary) {    NSString *obj = (NSString *)aDictionary[key];}
  • 如果改用基于快的方式来遍历,那么就可以在块方法签名中直接转换:
NSDictionary *aDictionary = /* ... */[aDictionary enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString *  _Nonnull obj, BOOL * _Nonnull stop) {    // Do something with 'key' and 'obj'}];
  • 基于块的遍历方式同样可以执行反向遍历,通过NSEnumeratorReverse选项来实现,要注意:只有在遍历数组或有序set等集合的时候才有意义,此处以数组为例。
NSArray *numbers = @[@1, @2, @3, @4, @5];[numbers enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {    NSLog(@"%@", obj);}];// 输出:5 4 3 2 1

要点

  • 遍历集合有四种方式。最基本的是for循环,其次是NSEnumerator遍历法及快速遍历法,最新、最先进的方式则是“块枚举法”。

  • 若提前知道遍历的集合含有何种对象,则应修改块签名,指出对象的具体类型。

0 0