第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来实现并处理。

上一篇下一篇

猜你喜欢

热点阅读