- 感谢以下文章作者的分享:
- iOS开发遍历集合(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、使用反向遍历,反向遍历删除元素后不会导致崩溃。