[学习笔记]_四种常用遍历方法(NSArray,NSDictionary,NSSet)

第一种方式:for循环

  • 遍历数组

    NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
    
    for (int i = 0; i < iosArray.count; i++) {
        
        //处理数组中数据
        
        NSLog(@"%@", iosArray[i]);
        
    }
    
    
  • 遍历字典

    NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
    
    NSArray *keysArray = [dict allKeys];
    
    for (int i = 0; i < keysArray.count; i++) {
    
    //根据键值处理字典中的每一项
    
    NSString *key = keysArray[i];
    
    NSString *value = dict[key];
    
    NSLog(@"%@", value);
    
    }
    
    
    • 因为字典和set是无序的,所以我们无法根据特定的整数下标来直接访问其中的值,于是需要先获取字典中的键或者set的所有对象,这样就可以在有序数组上进行遍历了。然而创建出多一个数组,会保留collection中的所有对象,占用了内存
  • 优缺点:

    • 优点:被广泛使用,容易接受,操作简单

    • 缺点:遍历字典和set比较繁琐,会占用比较多的系统资源


第二种方式:快速遍历


  • 遍历数组

    NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
    for (NSString*obj in iosArray) {
        
        //处理数组中的数据
        if ([obj isEqualToString:@"E"]) {
            NSLog(@"%@",obj);
        }
    }
    
    

    执行结果

    2016-09-02 11:35:38.553 bianli_demo[1914:78780] E
    
  • 遍历字典

NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};

for (NSString *key in dict) {
    if ([key isEqualToString:@"2"]) {
        NSLog(@"%@",dict[key]);
    }
}
```

执行结果

```
2016-09-02 11:35:38.553 bianli_demo[1914:78780] 22
```
- 反向遍历可以使用for(NSString *obj in [iosArray reverseObjectEnumerator])
  • 总结:
    • 优点:语法简洁,使用方便,效率高;
    • 缺点:
      • 1.无法方便获取当前遍历的下标
      • 2.无法再遍历过程中修改被遍历的collection,否则会导致崩溃

第三种方式:NSEnumerator


  • NSEnumerator是一个抽象基类,其中定义了2个方法,使其子类实现:

    (nullable ObjectType)nextObject;

    @property(readonly,copy)NSArray*allObjects;

    其中nextObject是关键方法,它返回枚举里的下一个对象,每次调用改方法,内部结构都会更新,使得下一次调用方法时能返回下一个对象,等到枚举中全部的对象都已经返回之后,再调用就会返回nil,表示达到了枚举的末端。

  • 遍历数组

    NSEnumerator *enumerator = [_iosArray objectEnumerator];//正向遍历
    
    id object ;
    
    while ((object = [enumerator nextObject])!= nil) {
        //处理枚举器中的数据
        NSLog(@"object = %@",object);
    }
    
    

    执行结果

    2016-09-02 12:03:25.043 get&post-test[2146:91583] object = L
    2016-09-02 12:03:25.044 get&post-test[2146:91583] object = O
    2016-09-02 12:03:25.044 get&post-test[2146:91583] object = V
    2016-09-02 12:03:25.044 get&post-test[2146:91583] object = E
    2016-09-02 12:03:25.044 get&post-test[2146:91583] object = I
    2016-09-02 12:03:25.044 get&post-test[2146:91583] object = O
    2016-09-02 12:03:25.044 get&post-test[2146:91583] object = S
    
    
  • 遍历字典

    NSEnumerator *enumerator2 = _dict.keyEnumerator;
    id obj2 ;
    while ((obj2= [enumerator2 nextObject]) != nil) {
        NSLog(@"obj2_keyValue = %@",_dict[obj2]);
    }
    

    执行结果

    2016-09-02 12:03:25.044 get&post-test[2146:91583] obj2_keyValue = 11
    

2016-09-02 12:03:25.045 get&post-test[2146:91583] obj2_keyValue = 22
2016-09-02 12:03:25.045 get&post-test[2146:91583] obj2_keyValue = 33
```

  • 总结
    • 优点:代码容易读,不需要定义额外的数组
- 缺点:
    
    1.无法直接获取遍历操作的下表,需要另外声明变量记录

    2.需要自行创建NSEnumerator对象。

第四种:基于块的遍历方式


  • 苹果封装的高效、优雅、易用的一套接口,笔者极力推荐的使用方法

  • 遍历数组

    self.iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
    /**
     *  遍历数组
     *
     *  @param obj  表示数组中元素
     *  @param idx  表示元素的下标
     *  @param stop 控制遍历何时停止,使用要在前面加*
     *
     *  @return
     */ 
    [_iosArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSLog(@"%@",obj);
        
        if ([obj isEqualToString:@"O"]) {
            
            *stop = YES;
        
        }
        
    }];
    
    

    执行结果

    2016-09-02 14:47:28.441 get&post-test[2282:121901] L
    2016-09-02 14:47:28.441 get&post-test[2282:121901] O
    
  • 遍历字典

    NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
    
    /**
     *  遍历字典
     *
     *  @param key  字典key值
     *  @param obj  字典value值
     *  @param stop 控制遍历何时停止
     *
     *  @return
     */
    [_dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
    
    NSLog(@"key = %@",key);
    NSLog(@"value = %@",obj);
    
    if ([key isEqualToString:@"2"]) {
        *stop = YES;
    }
    
    }];
    

    执行结果

    2016-09-02 14:54:38.816 get&post-test[2349:125565] value = 11
    2016-09-02 14:54:38.816 get&post-test[2349:125565] key = 2
    2016-09-02 14:54:38.816 get&post-test[2349:125565] value = 22
    
    

  • 注意
    • 若已知collection里对象的数据类型,可以修改块签名。知道对象的精确类型后,编译器就可以检测开发者是否调用了该对象所不具有的方法,并在发现问题是报错。

      NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
      
      //此处直接将key和value的类型改为nsstring
      [dict enumerateKeysAndObjectsUsingBlock:^(NSString key, NSString obj, BOOL * _Nonnull stop) {
      
      NSLog(@"%@", obj);
      
      if ([obj isEqualToString:@"22"]) {
      
      *stop = YES;
      
      }
      
      }];
      
      

  • 反向遍历

    //反向遍历数组
    [_iosArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       
        NSLog(@"%@",obj);
        if (obj == nil) {
            *stop = YES;
        }
        
    }];
    
    //反向遍历字典
    [_dict enumerateKeysAndObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString* key, NSString* obj, BOOL * _Nonnull stop) {
        
        NSLog(@"key = %@, value = %@",key,obj);
        
        if (key == nil) {
            *stop = YES;
        }
        
    }];
    

    执行结果

    2016-09-02 15:14:49.776 get&post-test[2527:135482] S
    2016-09-02 15:14:49.777 get&post-test[2527:135482] O
    2016-09-02 15:14:49.777 get&post-test[2527:135482] I
    2016-09-02 15:14:49.777 get&post-test[2527:135482] E
    2016-09-02 15:14:49.777 get&post-test[2527:135482] V
    2016-09-02 15:14:49.777 get&post-test[2527:135482] O
    2016-09-02 15:14:49.777 get&post-test[2527:135482] L
    2016-09-02 15:14:49.778 get&post-test[2527:135482] key = 1, value = 11
    2016-09-02 15:14:49.778 get&post-test[2527:135482] key = 2, value = 22
    2016-09-02 15:14:49.778 get&post-test[2527:135482] key = 3, value = 33
    

  • 并发遍历

    • 并发遍历:NSEnumerationConcurrent;也就是可以同时遍历collection的几个元素,具体数量根据系统资源而定,这样会充分利用系统资源,高效快捷的完成collection的遍历,系统底层会通过gcd来处理并发事宜,开发者不需担心内存和线程,其他方式若要实现高效的并发遍历十分有难度,通过块枚举遍历,改变collection不会引期代码崩溃

      NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
      
      NSMutableArray *iosMutableArray = [NSMutableArray arrayWithArray:iosArray];
      
      [iosMutableArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
      
      obj = [NSString stringWithFormat:@"_%@", obj];
      
      [iosMutableArray replaceObjectAtIndex:idx withObject:obj];
      
      NSLog(@"%@", obj);
      
      if ([obj isEqualToString:@"_I"]) {
      
      *stop = YES;
      
      }
      
      }];
      
      

      执行结果

      2016-09-02 15:27:06.123 get&post-test[2599:140840] _V
      2016-09-02 15:27:06.123 get&post-test[2599:140708] _L
      2016-09-02 15:27:06.124 get&post-test[2599:140708] _I
      2016-09-02 15:27:06.123 get&post-test[2599:140837] _E
      2016-09-02 15:27:06.124 get&post-test[2599:140708] _S
      2016-09-02 15:27:06.123 get&post-test[2599:140838] _O
      2016-09-02 15:27:06.124 get&post-test[2599:140840] _O
      
      
  • 总结

    • 优点
      • 1.可以完美实现for循环的所有功能。
      • 2.可以方便获取集合中的每一项元素。
      • 3.可以修改块签名
      • 4.可以实现并发循环功能,系统底层会通过gcd处理并发事宜
      • 5.效率高,能够提升程序性能,开发者可以专注于业务逻辑,而不必担心线程和内存问题
      • 6.提供了循环遍历的参数,NSEnumerationReverse用来实现倒序循环。NSEnumerationConcurrent用来实现并发遍历,两个参数可以同时使用;
    • 缺点
      • 1.这里使用了block,需要注意在block里容易引起的保留环问题,比如使用self调用方法时,把self转化成弱引用即可打破保留环。如__weak __typeof(self)weakSelf = self 或者 __weak MyController *weakSelf = self; 在block里使用weakSelf即可。

  • 注意

    使用基于块的遍历时是可以修改遍历的元素的,不会导致崩溃,但是如果
    要删除遍历的元素会导致后面的元素无法遍历而崩溃,解决办法有2种
    1、一种是复制一份原集合的副本,对副本进行操作,找出所要操作的元素后再处理原集合
    2、使用反向遍历,反向遍历删除元素后不会导致崩溃。
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容