Go to the previous, next section.

Enumerating the Contents of a Collection

Enumeration methods allow you to perform a specified operation on the members of a collection. You can operate on all the members, operate on all the members until a flag is set, operate only on the members that pass some test, or operate on the results of passing the members through a modifying function. Each of the enumerators come in pairs: one method for enumerating in place, and another method that is "safe" for enumerating when the operation changes the contents of the receiving collection. If the contents of the collection are changed during the enumeration you must use the "safe" methods.

Safe vs Unsafe Enumerating

The simple enumerators operate on the collection in-place. If the contents of the collection change during the enumeration, you must use the "safe" version of the enumerator instead.

It works by making a shallow copy of the receiver, performing the enumeration using the shallow copy, then freeing the shallow copy.

All enumerators have both "safe" and plain versions.

<<This section unfinished.>>

Basic Enumerating

The most basic enumerator is @deftypemethod Collection {} @keyword{-withObjectsCall:} (void(*)(id))func @deftypemethodx Collection {} @keyword{-safeWithObjectsCall:} (void(*)(id))func @deftypemethodx Collection {} @keyword{-withElementsCall:} (void(*)(elt))func @deftypemethodx Collection {} @keyword{-safeWithElementsCall:} (void(*)(elt))func Calls the function func with each the members of the collection. Returns self.

@deftypemethod {Collection} {(void*)} -newEnumState

@deftypemethod {Collection} {} -freeEnumState: (void**)enumStatePtr

@deftypemethod {Collection} {(BOOL)} -getNextObject: (id *)anObjectPtr @keyword{withEnumState:} (void**)enumState @deftypemethodx {Collection} {(BOOL)} -getNextElement: (elt *)anElementPtr @keyword{withEnumState:} (void**)enumState

void print_first_difference (id coll1, id coll2)
  {
    void *enumState1, *enumState2;
    id object1, object2;

    enumState1 = [coll1 newEnumState];
    enumState2 = [coll2 newEnumState];
    while ([coll1 getNextObject:&object1 withEnumState:&enumState1]
           && [coll2 getNextObject:&object2 withEnumState:&enumState2])
      {
        if ([object1 compare:object2])
          {
            [object1 printForDebugger];
            [object2 printForDebugger];
          }
      }
    [coll1 freeEnumState:&enumState1];
    [coll2 freeEnumState:&enumState2];
  }

@deftypemethod {Collection} {} -withElementsCall: (void(*)(elt))aFunc @keyword{whileTrue:} (BOOL*)flag @deftypemethodx {Collection} {} -safeWithElementsCall: (void(*)(elt))aFunc @keyword{whileTrue:} (BOOL*)flag

Keyed Enumerating

Indexed Enumerating

@deftypemethod {IndexedCollection} {(BOOL)} -getPrevObject: (id *) anObjectPtr @keyword{withEnumState:} (void**)enumState @deftypemethodx {IndexedCollection} {(BOOL)} -getPrevElement: (elt *) anElementPtr @keyword{withEnumState:} (void**)enumState

@deftypemethod {IndexedCollection} {} -withObjectsInReverseCall: (void(*)(id))aFunc @deftypemethodx {IndexedCollection} {} -safeWithObjectsInReverseCall: (void(*)(id))aFunc @deftypemethodx {IndexedCollection} {} -withElementsInReverseCall: (void(*)(elt))aFunc @deftypemethodx {IndexedCollection} {} -safeWithElementsInReverseCall: (void(*)(elt))aFunc

@deftypemethod {IndexedCollection} {} -withObjectsInReverseCall: (void(*)(id)) aFunc @keyword{whileTrue:} (BOOL *)flag @deftypemethodx {IndexedCollection} {} -safeWithObjectsInReverseCall: (void(*)(id)) aFunc @keyword{whileTrue:} (BOOL *)flag @deftypemethodx {IndexedCollection} {} -withElementsInReverseCall: (void(*)(elt)) aFunc @keyword{whileTrue:} (BOOL *)flag @deftypemethodx {IndexedCollection} {} -safeWithElementsInReverseCall: (void(*)(elt)) aFunc @keyword{whileTrue:} (BOOL *)flag

@deftypemethod {IndexedCollection} {} -makeObjectsPerformInReverse: (SEL) aSel @deftypemethodx {IndexedCollection} {} -safeMakeObjectsPerformInReverse: (SEL) aSel @deftypemethodx {IndexedCollection} {} -makeObjectsPerformInReverse: (SEL) aSel @keyword{with:} argObject @deftypemethodx {IndexedCollection} {} -safeMakeObjectsPerformInReverse: (SEL) aSel @keyword{with:} argObject

Defining Functions In Place with LAMBDA()

NOTE: The LAMBDA macro does not work on all systems. We are waiting for a general fix. You should avoid using it until then.

There may not already be a function that does the operation you want performed during an enumeration. In this case you will have to write such a function yourself.

If the function will be used more than once, you should write it as C functions are usually defined. However, if the function will only be used for this one enumeration and the function is small, using the LAMBDA macro may be easier and more clear.

Macro: LAMBDA(RETTYPE, ARGS, BODY)

LAMBDA is a macro for defining a nested function and returning a pointer to that function. You can use LAMBDA wherever a function pointer is required. RETTYPE is the C type returned by the function. ARGS is the parenthesis-enclosed list of arguments to the function. (Declare them just like in an ANSI function definition.) BODY is the curly-bracket-enclosed body of the function.

For example, you could create a pointer to a function that adds its arguments like this:

LAMBDA(int, (int a, int b), {return a + b;})

LAMBDA is particularly convenient for enumeration methods because the macro can be placed in the position of the method argument. For instance, you can write:

  - fooMethod
  {
    id newColl;
    ...
    newColl = [self emptyCopyAs:[Array class]];
    [self withObjectsCall:
          LAMBDA(void, (id o), {[newColl addObject:[o copy]];})];
    return self;
  }
instead of writing:
  - fooMethod
  {
    id newColl = [self emptyCopyAs:[Array class]];
    void myTmpFunc(id o)
      {
        [newColl addObject:[o copy]];
      }
    ...
    [self withObjectsCall:myTmpFunc];
    return self;
  }
In these examples, there may be many lines of code between the declarations at the top of the method and the withObjectsCall: at the bottom of the method. Using LAMBDA allows you to declare the function where it is used, instead of arbitrarily far from where it is used.

The name LAMBDA comes from lambda calculus and LISP.

The LAMBDA macro and some similar macros are defined in `coll/collstd.h'.

Filtered Enumerating

Enumerating with Keyed Collections

Enumerating with Indexed Collections

<<This section unfinished.>>

Go to the previous, next section.