48、多用块枚举,少用for循环

1、for循环

// 遍历NSArray
for (int i = 0; i < anArray.count; i++) {
  id object = anArray[i];
}
// 遍历NSDictionary
NSArray *keys = [aDic allKeys];
for (int i = 0; i < keys.count; i++) {
  id key = keys[i];
  id value = aDic[key];
}
// 遍历NSSet
NSArray *objects = [aSet allObjects];
for (int i = 0; i < objects.count; i++) {
  id object = objects[i];
}

​ 创建附加数组会有额外开销,而且还会多创建一组对象,它会保留collection中的所有元素对象。释放时这些附加对象也要释放,这样就调用了一些本来不需要执行的方法。其他各种遍历方式都无须创建这种中介数组。

2、使用OC1.0的NSEnumerator来遍历

​ NSEnumerator是一个抽象基类,其中自定义了两个方法,供子类实现:

- (NSArray *)allObjects;
- (id)nextObjects; // 返回枚举里的下一个对象

Foundation框架中内建的collection类都实现了这种遍历方式:`

// 遍历NSArray
NSEunmerator *enumerator = [anArray objectEnumerator]; // reverseObjectEnumerator 方向遍历
id object;
while ((object == [enumerator nextObject]) != nil) {/*...*/}
// 遍历NSDictionary
NSEnumerator *enumerator = [aDic keyEnumerator];
id key;
while ((key = [enumerator nextObject]) != nil) {
  id value = aDic[key];
}
// 遍历NSSet
NSEunmerator *enumerator = [aSet objectEnumerator];
id object;
while ((object == [enumerator nextObject]) != nil) {/*...*/}

3、快速遍历

​ OC2.0引入了快速遍历,为for循环开设了in关键字。从而大幅简化了遍历collectin所需的语法:

// 遍历NSArray
for (id object in anArray) {/*...*/}
// 遍历NSDictionary
for (id key in anDic) {
  id value = aDic[key];
}
// 遍历NSSet
for (id object in aSet) {/*...*/}
 支持快速遍历,遵从`NSFastEnumeration`协议就好,只有一个协议方法:
- (NSinterger)countBeyEnumeratingWithState:(NSFastEnumerationState *)state 
                                                                   objects:(id *)stackbuffer 
                                     count:(NSUInteger)length;

NSEnumerator对象也实现了NSFastEnumeration协议,所以能用来执行方向遍历:

for (id object in [anArray reverseObjectEnumerator])  {/*...*/}

4、基于块的遍历方式(遍历时可以获取更堵信息)

NSArray *anArray = [NSArray array];
[anArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  if (shouldStop) { 
    *stop = YES; // 优雅的终止遍历(其他的遍历可以用break终止)
  }
}];
NSDictionary *aDic = [NSDictionary dictionary];
[aDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
  if (shouldStop) {
    *stop = YES;
  }
}];
NSSet *aSet = [NSSet set];
// 可以修改方法签名,以免进行类型转换操作
[aSet enumerateObjectsUsingBlock:^(NSString *obj, BOOL * _Nonnull stop) { 
  if (shouldStop) {
    *stop = YES;
  }
}];

​ 可以利用另一个版本指向方向遍历、并发遍历:

// NSEnumerationConcurrent 并发的方式遍历
// NSEnumerationReverse 方向遍历
[anArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];

要点:

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

  • “块枚举法”本身就能通过GCD来并发执行遍历操作,无须另行编写代码。而采用其他遍历方式则无法轻易实现这一点

  • 若提前知道待比啊你了的collection含有何种对象,则应该修改块签名,指出对象的具体类型

Last updated

Was this helpful?