# How NSFastEnumeration Facilitates for…in Loops in MulleObjC

> Discover how NSFastEnumeration transforms for in loops in MulleObjC using countByEnumeratingWithState objects count for efficient batching without retention.

- Repository: [mulle-objc/mulleobjc](https://github.com/mulle-objc/mulleobjc)
- Tags: how-to-guide
- Published: 2026-03-07

---

**NSFastEnumeration provides the `countByEnumeratingWithState:objects:count:` method that the MulleObjC compiler uses to transform `for (id obj in collection)` syntax into a high-performance while loop that batches object pointers without retaining them.**

The mulle-objc/mulleobjc repository implements standard Objective-C fast enumeration through the `NSFastEnumeration` protocol. This mechanism allows any container class to expose its contents to the compiler’s `for…in` syntax by implementing a single state-driven batching method.

## The NSFastEnumeration Protocol and State Structure

In [`src/protocol/NSFastEnumeration.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/NSFastEnumeration.h), MulleObjC declares the protocol and the `NSFastEnumerationState` struct that drives the iteration:

```objc
typedef struct {
    unsigned long state;
    id *itemsPtr;
    unsigned long *mutationsPtr;
    unsigned long extra[5];
} NSFastEnumerationState;

@protocol NSFastEnumeration
- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state
                                 objects:(id *)buffer
                                   count:(NSUInteger)len;
@end

```

The **state** field acts as a cursor (often initialized to `0`), while **itemsPtr** points to a C array of `id` objects that the container fills. The **mutationsPtr** field references a mutation counter; if the collection changes during enumeration, the runtime can detect the mismatch and raise an exception. The header comments explicitly note that objects returned through this protocol are **not retained**, eliminating retain/release overhead inside the loop.

## How the Compiler Expands for…in Syntax

When the MulleObjC compiler encounters `for (id obj in container)`, it generates code equivalent to the following pattern:

1. **State initialization** – The compiler allocates an `NSFastEnumerationState` on the stack and zeroes it. On the first call to `countByEnumeratingWithState:objects:count:`, the container sets `state->itemsPtr` to its internal buffer (or the supplied `buffer`), stores a cursor in `state->state`, and assigns `state->mutationsPtr` to point to a mutation counter.

2. **Batch fetching** – The container copies up to `len` object pointers into the buffer and returns the actual count. A return value of `0` terminates the loop.

3. **Iteration** – The generated `while` loop walks the buffer returned in `state->itemsPtr`, assigning each element to the loop variable. Because the container vends direct pointers to its storage, the loop avoids per-object messaging overhead.

4. **Mutation guards** – If the container is modified during enumeration, the value at `*state->mutationsPtr` changes, allowing the runtime to throw an `NSGenericException` to prevent undefined behavior.

## Fast Enumeration in Built-in Collections

Core collection classes in MulleObjC adopt the protocol in [`src/protocol/NSContainer.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/NSContainer.h) and provide concrete implementations in their respective class files.

- **`src/class/NSArray.m`** implements the method to vend elements from its contiguous storage.
- **`src/class/NSDictionary.m`** enumerates keys (or key-value pairs depending on the internal representation).
- **`src/class/NSSet.m`** returns pointers to its hashed object storage.

Usage requires no special syntax beyond the standard `for…in` pattern:

```objc
#import <MulleObjC/NSArray.h>

NSArray *names = @[ @"Alice", @"Bob", @"Charlie" ];

for (id name in names) {
    printf("%s\n", [(NSString *)name UTF8String]);
}

```

Under the hood, the call to `countByEnumeratingWithState:objects:count:` happens automatically, fetching objects in batches sized to the stack buffer provided by the compiler.

## Implementing Custom Fast Enumeration

Any class can support `for…in` by adopting `NSFastEnumeration` and maintaining its own cursor. The following example from the MulleObjC source patterns demonstrates a minimal wrapper around a C array:

```objc
/* MyContainer.h */
@interface MyContainer : NSObject <NSFastEnumeration>
{
    id   *_storage;
    NSUInteger _count;
}
- (instancetype) initWithObjects:(id *)objects count:(NSUInteger)cnt;
@end

/* MyContainer.m */
@implementation MyContainer

- (instancetype) initWithObjects:(id *)objects count:(NSUInteger)cnt
{
    self = [super init];
    if (self) {
        _count = cnt;
        _storage = calloc(cnt, sizeof(id));
        memcpy(_storage, objects, cnt * sizeof(id));
    }
    return self;
}

- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state
                                 objects:(id *)buffer
                                   count:(NSUInteger)len
{
    /* First call initialization */
    if (state->state == 0) {
        state->state = (unsigned long)self;   // Store container reference
        state->mutationsPtr = &state->extra[0]; // Dummy mutation tracker
        state->extra[0] = 0;
    }

    NSUInteger idx = (NSUInteger)state->state;
    if (idx >= _count) {
        return 0;  // Enumeration complete
    }

    NSUInteger remaining = _count - idx;
    NSUInteger batch = (remaining < len) ? remaining : len;

    for (NSUInteger i = 0; i < batch; i++) {
        buffer[i] = _storage[idx + i];
    }

    state->itemsPtr = buffer;
    state->state = idx + batch;  // Advance cursor
    return batch;
}

@end

```

With this implementation, the container works seamlessly with fast enumeration:

```objc
id data[] = { @1, @2, @3 };
MyContainer *box = [[MyContainer alloc] initWithObjects:data count:3];

for (id item in box) {
    NSLog(@"Value: %@", item);
}

```

## Summary

- **NSFastEnumeration** in [`src/protocol/NSFastEnumeration.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/NSFastEnumeration.h) defines the single required method that powers `for…in` loops.
- The **NSFastEnumerationState** struct tracks iteration progress, provides a pointer to the current batch of objects, and monitors for mutations via `mutationsPtr`.
- Built-in collections (`NSArray`, `NSDictionary`, `NSSet`) implement the protocol in `src/class/NSArray.m`, `src/class/NSDictionary.m`, and `src/class/NSSet.m`, enabling zero-overhead enumeration.
- Custom containers adopt the protocol and implement **batching logic** inside `countByEnumeratingWithState:objects:count:` to support the same syntax without retaining enumerated objects.

## Frequently Asked Questions

### How does NSFastEnumeration improve performance compared to NSEnumerator?

**NSFastEnumeration** batches object pointers into a stack buffer and avoids per-object method calls. Unlike `NSEnumerator`, which requires a message send for every `nextObject` call, fast enumeration supplies multiple items in a single C array, reducing message overhead and eliminating retain/autorelease traffic for the enumerated items.

### What happens if a collection is modified during fast enumeration?

The container sets `state->mutationsPtr` to point at a mutation counter before returning objects. If the underlying collection changes (e.g., an item is added or removed), the counter increments. The MulleObjC runtime compares this value between batches and raises an `NSGenericException` if a mutation is detected, preventing inconsistent iteration state.

### Can I use fast enumeration with custom C data structures?

Yes. As long as your class adopts the `NSFastEnumeration` protocol and implements `countByEnumeratingWithState:objects:count:`, you can vend any data structure—C arrays, linked lists, or custom hash tables—through the `for…in` syntax. The protocol is storage-agnostic; it only requires that you fill the provided buffer with `id` pointers and track progress via `state->state`.

### Does the for…in loop retain the objects it enumerates?

No. According to the header comments in [`src/protocol/NSFastEnumeration.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/NSFastEnumeration.h), objects returned through the `itemsPtr` buffer are **not retained** by the fast enumeration machinery. This design keeps enumeration lightweight, but it means the container must ensure the objects remain valid for the duration of the batch (typically guaranteed because the container itself is retained by the caller for the lifetime of the loop).