前言
每次从自动售货机买汽水的时候,顾客投几个硬币进去,选择想要的汽水,然后它就被送到出货托盘。售货机中至少有两个主要部件在完成这个工作:
- 容纳一堆汽水的内部货架
- 从一堆汽水中取出下一瓶的分配器
在面向对象软件中,内部货架就像是一个集合,有多种分配器可以枚举数据结构中的数据,也就是发放内部货架中的瓶子,这种针对抽象集合迭代行为的设计模式就叫做迭代器。
什么是迭代器
迭代器提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。遍历集合中元素的职能从集合本身转移到迭代器对象,其提供了一个用于访问集合元素并记录当前元素的接口,不同的迭代器可以执行不同的遍历策略。
List与ListIterator之间关系的类图.png List
定义了修改集合以及返回集合中元素个数的方法。ListIterator
保持让一个对 List
对象的引用,以便迭代器遍历结构元素中的元素并将其返回。ListIterator
定义了让客户端从迭代过程中访问下一项的方法。迭代器中有个内部的 _index
变量,记录集合中的当前位置。
外部迭代器和内部迭代器
外部迭代器 | 内部迭代器 |
---|---|
外部迭代器让客户端直接操作迭代过程,所以客户端需要知道外部迭代器才能使用。但是它为客户端提供了更多的控制。 | 客户端不需要知道任何外部迭代器,而是可以通过集合对象的特殊接口,或者一次访问同一个元素,或者向集合中的每个元素发送消息。 |
客户端创建并维护外部迭代器 | 集合对象本身创建并维护它的外部迭代器 |
客户端可以使用不同外部迭代器实现多种类型的遍历 | 集合对象可以在不修改客户端代码的情况下,选择不同的外部迭代器 |
什么时候使用迭代器
-
需要访问组合对象的内容,而不暴露其内部表示
-
需要通过多种方式遍历组合对象
-
需要提供一个统一的接口,用来遍历各种类型的组合对象
迭代器的优缺点
迭代器的优点
- 它支持以不同的方式遍历一个聚合对象。
- 迭代器简化了聚合类。
- 在同一个聚合上可以有多个遍历。
- 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
迭代器的缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
Cocoa 中的迭代器实现
NSEnumerator
Foundation
框架中的 NSEnumerator
实现了迭代器模式。抽象NSEnumerator
类的私有具体子类返回枚举器对象,这些对象依次遍历各种类型的集合——数组、集合、字典(值和键)——将集合中的对象返回给客户端。
集合类,如NSArray
、NSSet
和 ,NSDictionary
定义了返回适合集合类型的 NSEnumerator 子类实例的方法。所有枚举器都以相同的方式工作,可以在循环中向枚举器对象发送一条 nextObject
消息,从枚举器取得对象,该循环在nil
返回时退出。
-
NSEnumerator
的使用NSArray *array = @[@"instance1",@"instance2",@"instance3"]; NSEnumerator *itemEnumerator = [array objectEnumerator]; NSString *item; while (item = [itemEnumerator nextObject]) { NSLog(@"%@",item); }
-
基于
Block
的枚举器NSArray *array = @[@"instance1",@"instance2",@"instance3"]; [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"%@",obj); }];
-
GNUStep 中的
NSArrayEnumerator
实现
GNUStep 中NSArrayEnumerator
实现如下,每次对索引pos++
,通过objectAtIndex
实现每次调用nextObject
获取下一个对象。oaiSel = @selector(c:); countSel = @selector(count); - (id) initWithArray: (NSArray*)anArray { self = [super init]; if (self != nil) { array = anArray; pos = 0; get = [array methodForSelector: oaiSel]; cnt = (unsigned (*)(NSArray*, SEL))[array methodForSelector: countSel]; } return self; } /** * Returns the next object in the enumeration or nil if there are no more * objects.<br /> * NB. modifying a mutable array during an enumeration can break things ... * don't do it. */ - (id) nextObject { if (pos >= (*cnt)(array, countSel)) return nil; return (*get)(array, oaiSel, pos++); }
快速枚举
快速枚举是 Objective-C 2.0 中引入的一种语言特性,它提供了一种简洁的语法来有效地枚举集合,比传统使用 NSEnumerator
对象来遍历数组、集合和字典要快得多。
要想使用快速枚举,需要实现 <NSFastEnumeration>
协议。Foundation 集合类—— NSArray
、NSDictionary
、 和 NSSet
,以及 NSEnumerator
类都实现了协议,支持快速枚举。
NSArray *array = @[@"instance1",@"instance2",@"instance3"];
for (NSString * item in array)
{
NSLog(@"%@",item);
}
内部枚举
NSArray
有个实例方法 makeObjectsPerformSelector:
,它允许客户端向数组中每个元素发送一个消息,让其执行指定的 aSelector
(必须实现了该方法)。
总结
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
网友评论