第48条:多用块枚举,少用for循环
2018-10-16 本文已影响13人
MrSYLong
在编程中经常需要列举collection中的元素,当前Objective-C语言有多种办法实现此功能。
第一种:for循环
NSArray *array = /* ... */;
for (int i = 0; i < array.count; i++) {
id object = array[i];
// Do something with 'object'
}
NSDictionary *dictionary = /* ... */;
NSArray *keys = [dictionary allKeys];
for (int i = 0; i < keys.count; i++) {
id key = keys[i];
id value = dictionary[key];
// Do something with 'key' and 'value'
}
NSSet *aSet = [NSSet setWithObjects:@"12",@"23",@"34",@"45",nil];
NSArray *objects = [aSet allObjects];
for (int i = 0; i < objects.count; i++) {
id object = objects[i];
// do something
}
由于字典和set都是无序的,所以遍历它们需要额外创建一个数组。
第二种方法:使用NSEnumerator来遍历
NSArray *array = /* ... */;
NSEnumerator *enumerator = [array objectEnumerator];
id object;
while ((object = [enumerator nextObject]) != nil) {
// Do something with 'object'
}
NSDictionary *dictionary = /* ... */;
NSEnumerator *enumerator = [dictionary keyEnumerator];
id key;
while ((key = [enumerator nextObject]) != nil) {
id value = dictionary[key];
// Do something with 'key' and 'value'
}
NSSet *aSet = [NSSet setWithObjects:@"12",@"23",@"34",@"45",nil];
NSEnumerator *enumerator = [aSet objectEnumerator];
id object;
while ((object = [enumerator nextObject]) != nil) {
// do something
}
这种方法与标准for循环相比,优势在于无论遍历哪种容器,语法都十分类似。
如果需要反向遍历,也可以获取反向枚举器:
NSArray *array = /* ... */;
NSEnumerator *enumerator = [array reverseObjectEnumerator];
第三种:Objective-C 2.0引入的快速遍历
与使用NSEnumerator类似,而语法更简洁,它为for循环开始了in关键字。
NSArray *array = /* ... */;
for (id object in array){
// Do something with 'object'
}
NSDictionary *dictionary = /* ... */;
for (id key in dictionary){
id value = dictionary[key];
// Do something with 'key' and 'value'
}
NSSet *aSet = [NSSet setWithObjects:@"12",@"23",@"34",@"45",nil];
for (id object in aSet) {
// do something
}
如果某个类的对象支持快速对象,只需要遵守NSFastEnumeration协议,该协议只定义了一个方法:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerarionState*)state object:(id*)stackbuffer count:(NSUInteger)length
由于NSEnumerator也实现了NSFastEnumeration协议,所以反向遍历可以这样实现:
NSArray *array = /* ... */;
for (id object in [array reverseObjectEnumerator]){
// Do something with 'object'
}
在目前所介绍的方法中这种方法最简单最高效。但缺点有两个,一是遍历字典时不能同时获取键和值,需要多一步操作,二是此方法无法轻松获取当前遍历操作所针对的下标(有可能会用到)。
第四种:基于块的遍历方式
NSArray *array = /* ... */;
// 参数:当前迭代所针对的对象、所针对的下标、指向布尔值的指针
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// Do something with 'object'
if (shouldStop) {
*stop = YES;
}
}];
NSDictionary *dictionary = /* ... */;
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// Do something with 'key' and 'value'
if (shouldStop) {
*stop = YES;
}
}];
NSSet *aSet = [NSSet setWithObjects:@"12",@"23",@"34",@"45",nil];
[aSet enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
// do something
}];
此方式的优势在于,遍历时可以直接从块里获取更多信息,并且能够通过修改块的方法名,避免进行类型转换操作。若已知字典中的对象必为字符串:
NSDictionary *dictionary;
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
// Do something with 'key' and 'value'
}];
此方法也可以传入选项掩码来执行反向遍历
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// Do something with 'object'
}];
在options处传入NSEnumerationConcurrent,可开启并行执行功能,通过底层GCD来实现并处理。