Implementing NSFastEnumeration for a custom class requires us to implement one method which will be called when we use for (var in coll) // { .. } form. Let's say we have a class DLList which is backed by an array as its main data source. For iterating elements in the DLList object, we can do as follows.
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable __unsafe_unretained [])buffer count:(NSUInteger)len {
if (state->state == [self count]) return 0;
__unsafe_unretained id const *arr = &_array; // (1)
state->itemsPtr = (__typeof__(state->itemsPtr))arr; // (2)
state->mutationsPtr = &state->extra[0]; // (3)
state->state = [self count];
return state->state;
}
Here state->itemsPtr requires a pointer to any object of type id. We use _array and pointer to the _array can be obtained by getting the address, &_array. But holding a reference to the variable will change its retain count, which we do not want. So in statement marked (1) we use __unsafe_unretained id const * as the type. We don't check for mutation here (3) as the collection is not being mutated during enumeration.
The Clang documentation on Objective-C Automatic Reference Counting (ARC) discusses the semantics of casts under ARC.
A program is ill-formed if an expression of type
T*is converted, explicitly or implicitly, to the typeU*, whereTandUhave different ownership qualification, unless:
Tis qualified with__strong,__autoreleasing, or__unsafe_unretained, andUis qualified with bothconstand__unsafe_unretained; or
In statement marked (2), we then typecast it to the type of state->itemsPtr which is same as __unsafe_unretained id * removing the const, which works because the ownership is the same.
The DLList class snippet is given below.
@implementation DLList {
NSMutableArray *_array;
}
- (NSUInteger)count {
return [_array count];
}
// ..
Now we can use fast enumeration like:
DLList *list = [[DLList alloc] initWithArray:@[@(1), @(2), @(3)]];
NSNumber *num = nil;
for (num in list) { // fast enumeration which will call the protocol method
NSLog(@"Elems: %@", num);
}