Chapter 14. Iterator

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.

What Is the Iterator Pattern?

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.

A class diagram depicts a relationship between a List and a ListIterator.

Figure 14-1. A class diagram depicts a relationship between a List and a ListIterator.

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.

A class diagram that shows a more in-depth relationship between abstract lists and iterators

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.

Note

ITERATOR: Provide a way to access to the elements of an aggregate object sequentially without exposing its underlying representation.[12]

When Would You Use the Iterator Pattern?

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.

Using Iterators in the Cocoa Touch Framework

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.

NSEnumerator

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.

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.

Fast Enumeration

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.

Internal Enumeration

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.

Enumerating Vertices of a Scribble

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.

A class diagram shows the relationships among Client, Mark, Stroke, NSEnumerator, and MarkEnumerator.

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.

Note

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.

Enumerating Vertices of a Scribble (Internally)

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.

A class diagram of a modified version of the MarkEnumerator and Stroke that implements an internal iterator with an introduction of MarkEnumerationBlock

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.

Example 14-9. The enumerateMarksUsingBlock: Method Implementation in Stroke.m

- (void) enumerateMarksUsingBlock:(void (^)(id <Mark> item, BOOL *stop)) block
{
  BOOL stop = NO;

  NSEnumerator *enumerator = [self enumerator];

  for (id <Mark> mark in enumerator)
  {
    block (mark, &stop);
    if (stop)
      break;
  }
}

Summary

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).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.135.36