Every time you buy a soda from a soda machine, you pop some coins in it, select the type of soda you want, and then it is delivered in the delivery tray.
Obviously, there is more than one bottle stored inside the soda machine. We consumers just won't know how those bottles are organized in the big tin box and how they are dispensed unless we crack the machine open to see what's inside. Otherwise, every time we buy a bottle of soda, we can only expect the machine to dispense the next bottle. The dispenser software running in the machine knows all the details about which bottle should next be dispensed among the whole collection of them available.
There are at least two main parts in a soda machine that do the job:
An internal crate that contains a collection of soda
A dispenser that extracts the next bottle from the collection
The internal crate doesn't know about dispensing, as its sole function is to "contain" bottles. It counts on a dispenser to do all the actual dispensing and delivery chores. Unless the soda machine has a transparent casing, we will never know what's involved in the process.
The internal crate is like a model in an MVC object structure, or simply a data structure. Its sole duty is to maintain a collection of data (bottled soda), not anything else. There could be many ways to enumerate the data (dispense the bottles) in the data structure (the internal crate). If we jam multiple ways of dispensing bottles in the internal tray, the system could be very complicated and difficult to maintain. So it would be better to use a separate mechanism to dispense (enumerate) bottles, especially if a vending machine manufacturer wants to reuse the same dispensing mechanism for other models of soda machines.
A design pattern for that iterative behavior for an abstract collection in object-oriented software is called Iterator. In this chapter, we are going to discuss the idea of the pattern as well as implement various types of iterators with the Cocoa Touch Foundation framework.
An iterator provides a way to access the elements of an aggregate object (a collection) sequentially without exposing its underlying representation and details of the structure. The responsibility of traversing the elements of a collection is transferred from the collection itself to an iterator object. The iterator defines an interface for accessing collection elements and keeps track of the current element. Different iterators can carry out different traversal policies.
A basic relationship between a collection and an iterator can be seen in a class diagram in Figure 14-1.
The List
defines methods to modify the collection as well as a number of items in it. The ListIterator
maintains a reference to a List
object so the iterator can walk through the elements in the structure and return them. The ListIterator
defines methods that allow clients to access the next item from the iteration process. The iterator has an internal index
_ variable to keep track of the current position of the collection. A more elaborated relationship between an aggregate and an iterator is shown in a class diagram in Figure 14-2.
Figure 14-2. A class diagram that shows a more in-depth relationship between abstract lists and iterators
Abstract Aggregate
defines a method, createIterator
, to return an Iterator
object. A ConcreteAggregate
object subclasses Aggregate
and overrides its createIterator
method and returns an instance of ConcreteIterator
. The Iterator
abstract class defines the basic iterative behavior of what any Iterator
should have. Clients will use the defined abstract interfaces to traverse elements in any type of Aggregate
object.
ITERATOR: Provide a way to access to the elements of an aggregate object sequentially without exposing its underlying representation.[12]
You'd naturally think of using the Iterator pattern when
You need to access contents of a composite object without exposing its internal representation.
You need to traverse composite objects in multiple ways.
You need to provide a uniform interface for traversing different types of composite objects.
In the following sections, we are going to implement the pattern with iterators that traverse scribbles in the TouchPainter example app that we saw in Chapter 2.
Apple has adapted the Iterator pattern with its naming convention of "enumerator/enumerate" for various methods in related Foundation classes. From now on, I will use enumerat* as Apple's version of iterat*. They mean the same thing as far as the pattern goes. The NSEnumerator
class in the Foundation framework implements the Iterator pattern. The private, concrete subclass of the abstract NSEnumerator
class returns enumerator objects that sequentially traverse collections of various types—arrays, sets, dictionaries (values and keys)—returning objects in the collection to clients.
NSDirectoryEnumerator
is a distantly related class. Instances of this class recursively enumerate the contents of a directory in the file system.
The collection classes such as NSArray
, NSSet
, and NSDictionary
define methods that return an instance of the NSEnumerator
subclass appropriate to the type of collection. All enumerators work in the same manner. You can retrieve an object from an enumerator by sending it a nextObject
message in a loop until it returns nil
to signal the end of traversal.
Since iOS 2.0, NSEnumerator
has been available for enumerating elements in NSArray
, NSDictionary
, and NSSet
objects. The NSEnumerator
itself is an abstract class. It relies on some factory methods, (see the Factory Method pattern, Chapter 4) like objectEnumerator
or keyEnumerator
, to create and return a corresponding concrete enumerator object. Clients use a returned enumerator object to traverse elements in a collection, as illustrated in the following code snippet.
NSArray *anArray = ... ; NSEnumerator *itemEnumerator = [anArray objectEnumerator]; NSString *item; while (item = [itemEnumerator nextObject]) { // do something with the item }
We assume the anArray
is storing a collection of NSString
objects. We process each item
in the while
loop with operations in NSString
.
When the array's content is exhausted, the message call [itemEnumerator nextObject]
will return nil
and the enumeration process is complete.
With the introduction of iOS 4.0, there is another method to enumerate collection objects of the Cocoa Touch framework. It's called Block-Based Enumeration.
The Block-Based Enumeration was introduced in the iOS 4 for collection objects in the Cocoa Touch framework. Block is part is the language features of Objective-C (as of this writing, Apple is still trying to standardize block as an extension to the C language). A block is a typed function, which means a block is a function yet it's also a type. A defined block is a variable that can be passed around between method calls just like other variables in an object. At the same time, a block variable can be used as a function within a method. When a method is passed with a block as a parameter, the block can be used as a callback function just like a function pointer in a C program. So the idea of block is perfect for implementing internal iterators (enumerators). Clients do not need to create iterators manually anymore but just provide a defined block that fits the required signature of the target collection object. Then the block will be called upon in each traversal step. An algorithm defining the block can process a returned element each time when the block is called by the target collection object.
Block is a really cool feature in the Objective-C language. It allows us to define callback algorithms in line within the same message call. Without using Block, the traditional way to do "callback" in the Cocoa Touch framework is to use delegation (see the Adapter pattern, Chapter 8). You need to define a separate protocol (a target) for any object (an adapter) that will respond to a callback from a client. If that part of an application is complex enough to justify another adapter mechanism, then that's fine. Sometimes a block can provide a more elegant solution than enumerators.
Apple has introduced new methods for the Block-Based Enumeration in NSArray
, NSDictionary
, and NSSet
objects in iOS 4. One of them is called enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
. You can define your own algorithm for the block in line with the message call, or predefine a block elsewhere, then pass it as a parameter in the message call. The following code snippet illustrates how it can be accomplished in code with an NSArray
object.
NSArray *anArray=[NSArray arrayWithObjects:@"This", @"is", @"a", @"test", nil]; NSString *string=@"test"; [anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { if([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) { // do something else with the returned obj *stop=YES; } } ];
When one of the words in the anArray
object is @"test"
, then set the pointer *stop=YES
to tell the anArray
object to stop the enumeration early. Except id obj
and BOOL *stop
parameters for the block, there is also the NSUInteger index
parameter. The index
parameter lets an algorithm in the block know the position of the current element, and it's very useful for concurrent enumeration like this. Without the parameter, the only way to access the index would be to use the indexOfObject:
method, which is inefficient.
The Block-Based Enumeration in an NSSet
object is very similar to the one in an NSArray
object, except the block signature doesn't have the index
parameter as part of the callback process. An NSSet
object is a data structure that simulates a set data structure; each element in a set doesn't have an index to refer the element's position in the structure.
A big benefit of using an internal iterator of NSArray
, NSDictionary
, and NSSet
is that an algorithm that processes their contents can be defined elsewhere by different developers. Unlike algorithms defined in a traditional for
loop, well-defined blocks can be reusable. When blocks are getting very large, we can put them in separate implementation files without crowding other code. Even though block is a convenient way to put inline algorithms without defining separate delegation protocols to complicate things, you should consider using the Strategy pattern (Chapter 19) for blocks that are too big to maintain.
Objective-C 2.0 offers a type of enumeration called Fast Enumeration. It's the preferred method to do enumeration by Apple. It allows an enumeration of a collection object to be used as part of a for
loop directly without using other enumerator objects, and it's more efficient than traditional index-based for
loops. The syntax of Fast Enumeration is as follows.
NSArray * anArray = ... ; for (NSString * item in anArray) { // do something with the item }
The enumeration loop now uses pointer arithmetic, which makes it more efficient than the standard methods of using NSEnumerator
to achieve the same thing.
In order to utilize Fast Enumeration, a collection class needs to implement NSFastEnumeration
protocol to provide necessary information regarding the underlying collection to the runtime. All collection classes as well as NSEnumerator
in the Foundation framework support Fast Enumeration. So instead of enumerating each item from an NSEnumerator
in a while
loop until nextObject
returns nil
, you can use its Fast Enumeration version, like the following code snippet.
NSArray * anArray = ... ; NSEnumerator * itemEnumerator = [anArray objectEnumerator]; for (NSString * item in itemEnumerator) { // do something with the item }
Even though you can use Fast Enumeration with a collection object as well as its enumerators, it would make more sense to fast enumerate a collection object directly if you need only a default traversal (mostly just by ascending order). NSEnumerator
uses its nextObject
method to implement the NSFastEnumeration
protocol. Performance-wise, it might not be a whole lot better than just calling that method manually in a while
loop. The for
loop in Fast Enumeration seems cleaner than the traditional while
loop with nextObject
, though.
Implementing NSFastEnumeration
is outside the scope of this book, so we will not discuss it here.
NSArray
has an instance method called (void)makeObjectsPerformSelector:(SEL)aSelector
, which allows clients to send a message to each item in the array so each of them executes a provided aSelector
(given an item supports it). You can achieve the same thing with one of those enumeration methods mentioned previously to let each of them execute the same selector. The method enumerates the underlying collection internally and sends a performSelector:
message to each item. The downside of this approach is if any one of the items in the collection doesn't respond to the selector, an exception will be thrown. So it's mostly good for some simple operations without much runtime checking.
In the TouchPainter app that we discussed in Chapter 2, there is a composite data structure that contains all touched points that are used for drawing strokes on the screen. The structure has an abstract type called Mark
. Its implementer is Stroke
. A detailed discussion on Mark
and the Composite pattern is in Chapter 13. Mark
defines the behaviors of a part-whole composite structure (a tree) that contains individual dots and strokes with vertices. The part-whole structure doesn't have any logic as to how it's supposed to be traversed. Because it's a tree, it can be traversed in different orders. If we hard-coded any traversal behaviors in the tree, there'd be a lot of brute-force changes to the interfaces and implementations of Mark
as well client code. A better solution than mixing traversal strategies in a tree structure is to add a factory method to Mark
(see the Factory Method pattern, Chapter 4), so its implementers will create and return an appropriate enumerator to traverse the tree in a specific order. We are going to implement an iterator (enumerator) for Stroke
by subclassing NSEnumerator
. And we'll call it MarkEnumerator
, which traverses a Mark
composite tree post-orderly. NSEnumerator
has two abstract methods, allObjects
and nextObject
. allObjects
returns an array of un-traversed Mark
instances in the composite, while nextObject
returns the next item in the roll. A class diagram that illustrates the idea is shown in Figure 14-3.
Figure 14-3. A class diagram shows the relationships among Client
, Mark
, Stroke
, NSEnumerator
, and MarkEnumerator
.
Before we get to MarkEnumerator
, let's first take a quick look at Mark
so we'll have a better idea about how MarkEnumerator
can traverse its children. We need to add a factory method called enumerator
to Mark
so its implementers can use that method to return an instance of an NSEnumerator
's subclass (i.e., MarkEnumerator
), as shown in Listing 14-1.
Example 14-1. Mark.h
@protocol Mark <NSObject>
@property (nonatomic, retain) UIColor *color;
@property (nonatomic, assign) CGFloat size;
@property (nonatomic, assign) CGPoint location;
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) id <Mark> lastChild;
- (void) addMark:(id <Mark>) mark;
- (void) removeMark:(id <Mark>) mark;
- (id <Mark>) childMarkAtIndex:(NSUInteger) index;
- (NSEnumerator*) enumerator;
@end
MarkEnumerator
will perform post-order traversal on a Mark
tree structure and enumerate each item at a time. So we are going to use a stack to do that. However, there is no ready-to-use stack class available from the Foundation framework. We will need to DIY it ourselves. But before we get to that, let's continue with the class definition of MarkEnumerator
. Even though we are not required to re-declare overridden methods in subclasses in Objective-C because of its dynamic binding runtime, it's still a good practice to do so. The class definition of MarkEnumerator
with overridden NSEnumerator
methods is shown in Listing 14-2.
Example 14-2. MarkEnumerator.h
#import "NSMutableArray+Stack.h" #import "Mark.h" @interface MarkEnumerator : NSEnumerator { @private NSMutableArray *stack_; } - (NSArray *)allObjects; - (id)nextObject; @end
Based on our class design, a MarkEnumerator
object is supposed to be created and initialized by one of the Mark
's implementing classes. At the same time, a MarkEnumerator
needs to know what Mark
it needs to work on at the time of creation, so we need to define a private initWithMark:
method in its anonymous category, as in Listing 14-3.
Example 14-3. An Anonymous Category That Declares Private Methods in MarkEnumerator+Private.h
@interface MarkEnumerator () - (id) initWithMark:(id <Mark>)mark; - (void) traverseAndBuildStackWithMark:(id <Mark>)mark; @end
The reason we put the private methods in an anonymous category is that we wanted the implementation for them in the main @implementation
block as well. In that private category, we also define another private method for traversing a Mark
composite object. Although there are both public and private methods defined in two different places, their implementations are living in the same .m
file, as in Listing 14-4.
Example 14-4. MarkEnumerator.m
#import "MarkEnumerator.h" #import "MarkEnumerator+Internal.h" @implementation MarkEnumerator - (NSArray *)allObjects { // returns an array of yet-visited Mark nodes // i.e. the remaining elements in the stack return [[stack_ reverseObjectEnumerator] allObjects]; } - (id)nextObject { return [stack_ pop];; } - (void) dealloc { [stack_ release]; [super dealloc]; } #pragma mark - #pragma mark Private Methods - (id) initWithMark:(id <Mark>)aMark { if (self = [super init]) { stack_ = [[NSMutableArray alloc] initWithCapacity:[aMark count]]; // post-orderly traverse the whole Mark aggregate // and add individual Marks in a private stack [self traverseAndBuildStackWithMark:aMark]; } return self; } - (void) traverseAndBuildStackWithMark:(id <Mark>)mark { // push post-order traversal // into the stack if (mark == nil) return; [stack_ push:mark]; NSUInteger index = [mark count];
id <Mark> childMark; while (childMark = [mark childMarkAtIndex:--index]) { [self traverseAndBuildStackWithMark:childMark]; } } @end
In its private initWithMark:
method, it uses a reference to an incoming Mark
reference and then fires up its own traverseAndBuildStackWithMark:(id <Mark>)mark
method to traverse mark
. The method calls itself recursively and pushes mark
as well as any of its children in the stack (post-order traversal construction in stack). Notice that the children are pushed in reverse order (right-to-left). When we visit the elements in the stack, the children will be retrieved in their original order (left-to-right). Now the stack has all elements in the Mark
aggregate, and it's ready to be used when a client sends the nextObject
message to a MarkEnumerator
object to retrieve the next Mark
in the collection.
You might have noticed that the nextObject
method has only one statement: return [stack_ pop];
. After a traversal with Mark
aggregate, elements that were pushed in a stack first will be popped out last. So the first child will be the first element returned by the nextObject
method. Their parent will be returned after all of its children. allObjects
is supposed to return an instance of NSArray
that contains a collection of unvisited elements. As the stack pops forward in the collection and any popped element will be removed from it, returning what's left in the stack in the ascending direction will be just right.
We are using a stack to help traverse a Mark
tree, but there is no such thing called NSStack
packed in the Foundation framework for us to use. So we need to hack one of the closest Foundation classes, NSMutableArray
, to make our own stack, as shown in Listing 14-5.
Example 14-5. NSMutableArray+Stack.h
@interface NSMutableArray (Stack) - (void) push:(id)object; - (id) pop; @end
We've added two methods to NSMutableArray
as a category to push and pop objects like what a real stack does. Its push
method pushes an object to the first position of itself, and pop
always returns the first item and removes it, as shown in Listing 14-6.
Example 14-6. NSMutableArray+Stack.m
#import "NSMutableArray+Stack.h" @implementation NSMutableArray (Stack) - (void) push:(id)object { [self addObject:object];
} - (id) pop { if ([self count] == 0) return nil; id object = [[[self lastObject] retain] autorelease]; [self removeLastObject]; return object; } @end
Now let's get back to the Mark
's family where we left off a little earlier. Stroke
is the only member in the family whose objects will contain children; so it implements the enumerator
method, while other family members don't. We omit them here for brevity. Its enumerator
method simply creates and initializes an instance of MarkEnumerator
with self
as a parameter, and then it returns the instance, as shown in Listing 14-7.
Example 14-7. Stroke.m
#import "Stroke.h" #import "MarkEnumerator+Internal.h" @implementation Stroke @synthesize color=color_, size=size_; - (id) init { if (self = [super init]) { children_ = [[NSMutableArray alloc] initWithCapacity:5]; } return self; } - (void) setLocation:(CGPoint)aPoint { // it doesn't set any arbitrary location } - (CGPoint) location { // return the location of the first child if ([children_ count] > 0) { return [[children_ objectAtIndex:0] location]; } // otherwise returns the origin return CGPointZero; } - (void) addMark:(id <Mark>) mark
{ [children_ addObject:mark]; } - (void) removeMark:(id <Mark>) mark { // if mark is at this level then // remove it and return // otherwise, let every child // search for it if ([children_ containsObject:mark]) { [children_ removeObject:mark]; } else { [children_ makeObjectsPerformSelector:@selector(removeMark:) withObject:mark]; } } // needs to be added to draft - (id <Mark>) childMarkAtIndex:(NSUInteger) index { if (index >= [children_ count]) return nil; return [children_ objectAtIndex:index]; } // a convenience method to return the last child - (id <Mark>) lastChild { return [children_ lastObject]; } // returns number of children - (NSUInteger) count { return [children_ count]; } - (void) dealloc { [color_ release]; [children_ release]; [super dealloc]; }#pragma mark
-#pragma mark enumerator method
-(NSEnumerator *) enumerator
{
return [[[MarkEnumerator alloc] initWithMark:self] autorelease];
}
@end
Since enumerator
is a factory method, it can return an object of a different MarkEnumerator
subclass without modifications to client code. If we want the factory method to support different traversal methods, we can have the method accept a parameter that specifies the type of traversal so it can select a different MarkEnumerator
at runtime.
It can be dangerous to modify an aggregate object while you're traversing it. If elements are added or deleted from the aggregate, you might end up accessing an element twice or missing it completely. A simple solution is to make a deep copy of the aggregate and traverse the copy, but it might be expensive if making and storing another copy of the aggregate can cause performance implications.
There are many ways to implement iterators that wouldn't be interfered with by element insertions and removals. Most rely on registering the iterator with the aggregate. One way to achieve that is on insertion or removal; the aggregate either adjusts the internal state of iterators it has produced, or it maintains information internally to ensure proper traversal.
The iterator (enumerator) that we have discussed so far is an external iterator that we have seen in the original description of the Iterator pattern. It needs a client to ask the collection object to return its iterator, and then loop through the iterator to visit each of its elements. The client each time sends a nextObject
message to the iterator to retrieve an element in a loop until the collection is exhausted.
Without letting the client use any iterator (enumerator) directly, we can implement an internal iterator as another approach. The internal or passive iterator (usually the collection object itself) controls the iteration. You can implement an internal iterator by letting clients provide some sort of callback mechanism to the internal iterator so it can return the next element in a collection when it's ready.
Since the introduction of iOS SDK 4.0, we can use Objective-C blocks for application development. A block is a form of function type. Once it is defined, it can be reused anywhere in the application. Blocks are much more powerful than C function pointers in many ways. They are a natural choice for implementing internal iterators in Objective-C.
The class diagram in Figure 14-4 illustrates a possible way to implement an internal iterator for the Mark
family.
Figure 14-4. A class diagram of a modified version of the MarkEnumerator
and Stroke
that implements an internal iterator with an introduction of MarkEnumerationBlock
We have added a new method to the Mark
, enumerateMarksUsingBlock:markEnumerationBlock
. A client will provide a defined block with a signature of ^(id <Mark> item, BOOL *stop)
as a parameter. The block defines an algorithm that processes each Mark
element returned from the internal iterator. If the algorithm wants to stop the enumeration at the current position, it can set the *stop
variable to YES
to do so. As discussed in the sidebar table, a composite object itself, instead of clients, maintains an internal iterator. The enumerateMarksUsingBlock:
method monitors the enumeration process with a loop that runs through an instance of MarkEnumerator
. Every element obtained from the enumerator will be passed to a provided block, so the algorithm in it will be able to process the element.
If you want to let clients use the internal iterator exclusively, then you can put the enumerator
factory method that returns an instance of MarkEnumerator
in an anonymous category, just like we did for the MarkEnumerator
class in Listing 14-3. It totally depends on the design.
Code snippets in Listings 14-8 and 14-9 show changes in both the Mark
protocol and the Stroke
class.
Example 14-8. Mark.h
@protocol Mark <NSObject> @property (nonatomic, retain) UIColor *color; @property (nonatomic, assign) CGFloat size; @property (nonatomic, assign) CGPoint location; @property (nonatomic, readonly) NSUInteger count; @property (nonatomic, readonly) id <Mark> lastChild; - (void) addMark:(id <Mark>) mark; - (void) removeMark:(id <Mark>) mark; - (id <Mark>) childMarkAtIndex:(NSUInteger) index; - (NSEnumerator*) enumerator;// for internal iterator implementation
- (void) enumerateMarksUsingBlock:(void (^)(id <Mark> item, BOOL *stop)) block;
@end
The implementation of the enumerateMarksUsingBlock:(void (^)(id <Mark> item, BOOL * stop)) block
in Stroke
is as follows.
The Iterator pattern is somewhat similar to the Visitor pattern (Chapter 15), especially if you push the traversal algorithms into the Visitor pattern or let an internal iterator execute operations on elements while traversing an aggregate.
Other related patterns are the Composite (Chapter 13), Factory Method (Chapter 4), and Memento (Chapter 23). The Composite often relies on iterators to traverse its recursive structure. Polymorphic iterators rely on factory methods to instantiate the appropriate iterator concrete subclasses. Sometimes, mementos are used in conjunction with the Iterator pattern. An iterator can use a memento to capture the state of iteration. The iterator stores the memento internally and restores its internal state later from it.
We have discussed some design patterns that are primarily related to abstract collection and its traversal. Next, we are going to talk about some other patterns that extend the behavior of abstract collection or other types of objects with minimal impact to the existing design.
[12] The original definition appeared in Design Patterns, by the "Gang of Four" (Addison-Wesley, 1994).
3.16.135.36