Cuckoo for Cocoa Dev

A blog for all those Cuckoo for Cocoa Development

Using Block Enumeration for Custom Collections

While the for-in loop is a great way to enumerate through collections such as NSArray, sometimes it’s lacking in other areas that force us to go back to using classical for() syntax. An easy example is the necessity of an index in said collection in addition to the object being enumerated. This is where block enumeration comes through to give us an easy way to traverse through the list as well as get other helpful information such as a stop variable, index in addition to the objects. While this is quite common, what developers tend to forget is that this block enumeration can also be added to custom objects and collections.

Before we dive into our own block enumeration, it’s helpful to look at how we use one for an NSArray:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSArray *numbers = @[@1, @2, @3, @4, @5];
[numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"%@ at index: %d", obj, idx);
        if ([obj isEqual:@3]) {
            *stop = YES;
            NSLog(@"Stoping enumeration after finding 3");
        }
}];

/*
Log message given:

1 at index: 0
2 at index: 1
3 at index: 2
Stopping enumeration after finding 3
*/

We can see above that the block passed into -enumerateObjectsUsingBlock: method gets called for each element until it finds 3.

For our example, we will add -enumerateObjectsUsingBlock: to a small simple linked list class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// Individual Node in Linked List
@interface SimpleNode : NSObject

@property (strong, nonatomic) SimpleNode *nextNode;
@property (strong, nonatomic) id item;

- (id)initWithItem:(id)item;

@end

@implementation SimpleNode

- (id)initWithItem:(id)item
{
    self = [super init];
    if (self) {
        self.item = item;
    }
    return self;
}

@end


// Simple Linked List using Simple Node
@interface SimpleLinkedList : NSObject

@property (strong, nonatomic) SimpleNode *headNode;
@property (nonatomic) NSInteger numberOfNodes;

- (void)addItem:(id)item;

@end

@implementation SimpleLinkedList

- (id)init
{
    self = [super init];
    if (self) {
        self.numberOfNodes = 0;
    }
    return self;
}

- (void)addItem:(id)item
{
    if (!self.headNode) {
        self.headNode = [[SimpleNode alloc] initWithItem:item];
    } else {
        [self addItem:item
                 node:self.headNode];
    }
    self.numberOfNodes++;
}

- (void)addItem:(id)item
           node:(SimpleNode *)node
{
    if (!node.nextNode) {
        node.nextNode = [[SimpleNode alloc] initWithItem:item];
    } else {
        [self addItem:item
                 node:node.nextNode];
    }
}

@end

We will begin by adding the -enumerateObjectsUsingBlock: method to the interface file:

1
2
3
4
5
6
7
@interface SimpleLinkedList : NSObject

// other methods and properties

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, BOOL *stop))block;

@end

Like the NSArray -enumerateObjectsUsingBlock: method, we will take a block that returns a void and has 2 parameters, the object in the linked list and a boolean stop. Since this is a linked list, usually we don’t need an index.

Next, we will provide the implementation of traversing through the list, calling the block for each element and setting an if statement to break out of the loop if stop is set to YES:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@implementation SimpleLinkedList

// other methods/property implementations

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, BOOL *stop))block
{
    SimpleNode *node = self.headNode;
    BOOL stop = NO;

    while (node) {
        block(node.item, &stop);

        if (stop) {
            break;
        }
        node = node.nextNode;
    }
}

@end

Let’s see the SimpleLinkedList enumeration block method in action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
NSMutableArray *oddNumbersLessThanFour = [@[] mutableCopy];

SimpleLinkedList *linkedList = [[SimpleLinkedList alloc] init];
[linkedList addItem:@1];
[linkedList addItem:@2];
[linkedList addItem:@3];
[linkedList addItem:@4];
[linkedList addItem:@5];
[linkedList addItem:@6];

[linkedList enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
  if ([obj intValue] % 2 != 0) {
      [oddNumbersLessThanFour addObject:obj];
    }

    if ([obj isEqual:@4]) {
      *stop = YES;
    }
}];

[oddNumbersLessThanFour enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  NSLog(@"%@ at index: %d", obj, idx);
}];

/*
Log message given:

1 at index: 0
3 at index: 1
*/

So there you go, a quick example of adding block based enumeration to a custom build class. It definitely comes in handy, and this simple example could be updated to provide options for reverse traversal or other block parameters that are useful for your own custom classes.

Comments