Enumerators

Enumerators provide a mechanism for accessing all of the objects in a collection sequentially
without exposing the collection’s underlying data structures. Enumerators are
used to write flexible and efficient code for using collection classes without tying applications
to specific implementations. Furthermore, enumerators provide a uniform interface
to collection classes that can be extended to meet a wide range of needs including alternative
traversal orders and algorithms.

 

Motivation
Enumerators provide a uniform means of traversing a collection’s underlying data structures.
The interface presented by Enumerators is independent of the traversal order and
algorithms used for traversal. Enumerators decouple a collection class from code that
traverses it. It is also possible to have multiple traversals active simultaneously, but Cocoa
generally disallows the modification of mutable collections while they are being traversed.

 

Using Enumerator Objects

In Cocoa, the Enumerator pattern is defined by the abstract NSEnumerator class. Nearly
every collection in Cocoa offers one or more methods that return an instance of a
concrete NSEnumerator subclass. Many other Cocoa objects also have methods to
return an NSEnumerator to iterate over relevant sets of objects. Most commonly the
-objectEnumerator method is used to obtain a relevant Enumerator.

For example, NSArray offers the two methods -objectEnumerator and
- reverseObjectEnumerator to obtain enumerators. Using the -objectEnumerator
method traverses the array’s contents in order from first element to last, starting with the
object at index 0, which is what is normally desired.To traverse the other direction, from
the end to the beginning of the array, use -reverseObjectEnumerator instead.The
NSDictionary class also offers the -objectEnumerator method for traversing a list of all
the contained objects.Alternatively, the -keyEnumerator method can be used to iterate over
the dictionary’s keys instead of the objects it contains.To find out what traversals are possible
for a particular collection, simply look for methods that return NSEnumerator objects.

The NSEnumerator class defines only two methods: -nextObject and -allObjects.
Usually only the -nextObject method is used.The first time it’s called, the first object in
the traversal is returned. Subsequent calls return objects one at a time until the list is exhausted.
Once all objects have been traversed, -nextObject returns nil.You typically
write code like the following to traverse an enumeration:

id instance;
NSEnumerator *enumerator = [myCollection objectEnumerator];
while (instance = [enumerator nextObject])
{
// do something with instance
}

 Use the -allObjects method to obtain an NSArray filled with all the objects
remaining to be enumerated.The -nextObject method always returns nil after the
-allObjects method has been used. 

 

Using Fast Enumeration

Fast enumeration was introduced in Objective-C 2.0 as a way to both simplify and potentially
speed up enumeration loops.With fast enumeration, the code from the previous
section is reduced to either of these loops:

id instance;
for (instance in myCollection)
{
// do something with instance
}
// or, alternatively:
for (id instance2 in myCollection)
{
// do something with instance2
}

  This decreases the amount of code written, making the programmer’s intent more
clear. In turn, the possibility of bugs is reduced. If the collection class implements fast
enumeration correctly, then using fast enumeration can be more efficient and safer. Because
modifying mutable collections while they are being traversed isn’t allowed in Cocoa,
fast enumeration offers some automatic safeguards to detect such activity and throw
exceptions when it occurs. Not all NSEnumerator subclasses properly detect mutation of
the data they are traversing, which can lead to unpredictable application behavior or
crashing if a collection is modified while it is being enumerated.

Typically the order of enumeration performed by fast enumeration is the same as the
order of enumeration performed by the NSEnumerator instance returned by a call to the
-objectEnumerator method. However, the NSEnumerator class itself supports fast enumeration
and can be used in the for () statement. For example, here is the code to enumerate
an NSArray in reverse with fast enumeration:

id instance;
for (instance in [myArrayInstance reverseObjectEnumerator])
{
// do something with instance
}

  

Creating Custom Enumerators

 

 Implementing Fast Enumeration pp92  ???

 

Internal Enumeration

 All of the enumeration that has been discussed here is what is known as external or active
iteration, meaning that the iteration loop is completely under control of the programmer
and external to the collection classes themselves. Cocoa does support another type of iteration,
known as internal or passive iteration. Internal or passive iteration is an implied
iteration; there is no explicit control over the loop itself, even though the existence of a
loop is implied.
For example, consider these two NSArray methods:
- (void)makeObjectsPerformSelector:(SEL)aSelector
- (void)makeObjectsPerformSelector:(SEL)aSelector
withObject:(id)anObject

 Both of these methods send the same message to every object in an array.They are a
shortcut that eliminates the need for an explicit enumeration loop.There is still an unseen
loop, of course; the latter of these methods is very likely implemented using fast enumeration
as follows:

- (void)makeObjectsPerformSelector:(SEL)aSelector
withObject:(id)anObject
{
id object;
for (object in self)
{
[object performSelector:aSelector withObject:anObject];
}
}

  

 Examples in Cocoa

It is nearly impossible to do much in Cocoa without encountering enumeration in some
form. Most collection classes such as NSArray, NSDictionary, NSSet, NSCountedSet,
NSHashTable, NSMapTable, and so on all implement the -objectEnumerator method to
return an Enumerator instance. Many collections have other methods that return other
Enumerators. NSArray has -reverseObjectEnumerator to perform a different traversal
order. NSDictionary and NSMapTable use -keyEnumerator for traversing their keys instead
of the contained objects.

Typically the same classes that can create and return Enumerators also support fast
enumeration. In general, when fast enumeration is used the traversal will be the same as if
the Enumerator returned by the -objectEnumerator method were used normally. Notable
exceptions to this are NSDictionary and NSMapTable, which iterate over their keys when
fast enumeration is used.

One interesting special case is the NSPointerArray, a new class introduced in 10.5,
which can store NULL values. NSEnumerator doesn’t work with NSPointerArray because
the first NULL returned during contents traversal ends the traversal.The NSPointerArray
class does, however, support fast enumeration.This works because fast enumeration implementations
return how many references are being batched together.This means that they
can return NULL values for some of the objects in the traversal without causing the loop
to be exited because loop termination conditions do not depend on the return of a NULL
or nil value.

 

Consequences

 

 

 

 

posted on 2013-02-22 01:27  Chansonyan  阅读(356)  评论(0)    收藏  举报

导航