A core feature of Cocoa is class clusters. Class clusters are a way of letting a developer mimic a built-in core type by subclassing it and overriding its fundamental methods, called “primitive methods”. Primitive methods are the key to how class clusters work. The simplest example of this is NSArray.

On NSArray all methods are guaranteed to be written in terms of the two primitive methods, -objectAtIndex: and -count. That means that if we can subclass NSArray and override those two methods, and get all of the other NSArray methods for free.

To illustrate, let’s use array reversal. The typical way in Cocoa of reversing an array is via the array’s reverseObjectEnumerator. It could go in a category:

@implementation NSArray (Reversing)

- (NSArray *)reversedArray {  
    return self.reverseObjectEnumerator.allObjects;  
}

@end

While this is a fine solution, if you examine the result of this, perhaps by checking its class in the debugger:

(lldb) po [self.reversedArray class]
__NSArrayM

You can see that it returns an __NSArrayM, which is shorthand for NSMutableArray. This means that the reversed array is not only mutable, but also, it represents a full copy of the old array. For long arrays, this can be expensive!

With class clusters, we can do something a little different. It’s a little bit more code, but it will reveal additional efficiency. Let’s reverse the array using a new class.

@interface ReversedArray : NSArray

@property (readonly) NSArray *arrayToReverse;
	
@end

@implementation ReversedArray
	
- (instancetype)initWithArray:(NSArray *)array {  
    self = [super init];  
    if (!self) return nil;
    	
    _arrayToReverse = [array copy];
    	
    return self;  
}
	
- (NSUInteger)count {  
    return self.arrayToReverse.count;  
}
	
- (id)objectAtIndex:(NSInteger)index {  
    return [self.arrayToReverse objectAtIndex:self.count - index - 1];  
}

@end  

That’s it. This is a fully-functioning NSArray. You can use array subscripting, -indexOfObject:, enumerators, sorting, all of it.

Add an extension, and you can use it exactly how you’d want:

@implementation NSArray (Reversing2)

- (NSArray *)reversedArray {  
    return [[ReversedArray alloc] initWithArray:self];
}

@end

At first blush, it’s an extreme solution to a simple problem. But there are certain advantages conferred here that I love.

First, this is a fully encapsulated implementation. The implementation can generate its backing array eagerly or lazily, depending on constraints of the device or the whims of the developer. The implementation details are hidden away, as it should be.

Second, we gain an edge with performance. The reverseObjectEnumerator makes a full copy of the array. For large arrays, the class clustered version will use less memory and take less execution time. While we do call -copy in our class cluster, assuming the array is already immutable that method will just return self. (If it’s not immutable, -copy will create a full copy of the array, and we’ve lost nothing with the new solution.)

If you’re still not convinced, you might be interested to know that Apple does it this way themselves. Try it out in your debugger. First, remove any categories that add -reversedArray. Then, attempt to reverse an array in the debugger.

(lldb) po [@[@1, @2, @3] reversedArray]
(3, 2, 1)  

You have to do this in the debugger, because the implementation of -reversedArray isn’t exposed in the NSArray headers, even thoughtit does exist (as of the iOS 9.0 SDK, at least). And calling it returns a specialized class, __NSArrayReversed:

(lldb) po [[array reversedArray] class]
__NSArrayReversed

This specialized version behaves just like the version we implemented above.

One other thing that specialized subclasses allow you do is implement requirements more efficiently. With class clusters, any methods that you want to further optimize you can just re-implement on the subclass. For example, reversing a reversed array is just the original array.

@implementation ReversedArray (Specializations)

- (NSArray *)reversedArray {
    return self.arrayToReverse;
}

@end

Class clusters and primitive methods exist for many core types, including NSArray, NSString, NSDictionary, and their mutable counterparts.

On NSString, methods like -stringByAppendingPathComponent: can return a specialized version of NSString that is backed by an array of path components, so that adding and removing path components is as easy as modifying that internal array. Let’s check if this is how it’s implemented:

(lldb) po [[@"a" stringByAppendingPathComponent:@"b"] class]  
NSPathStore2  

NSPathStore2 is a class-clustered NSString exhibits these propreties. The implemenation can be optimized so that this class doesn’t have to search for path separators (like “/” or “:”), and it can manipulate paths more easily. (I wonder what happened to NSPathStore1?) You can see all of the class clusters that Apple uses in this gist.

This is a cool tool for your toolbelt. What else can we use this technique for?

The project I’m currently working on has an SDK. I’ve used bad third-party SDKs before, and I wanted the standards for this project to be higher. I want the developers who are integrating this SDK into their project to rest easy, so the SDK has no categories at all. This was a problem for me, since I wanted to use -map: on my arrays.

This technique is how I got out of that pickle. A class-clustered array that is initialized with a “before” array and a transformation block is all this class needs.

@interface MappedArray ()

@property (nonatomic) NSArray *backingArray;
	
@end
	
@implementation MappedArray

- (instancetype)initWithArray:(NSArray *)array transformationBlock:(id (^)(id object))block {  
    self = [super init];  
    if (!self) return nil;
    	
    NSMutableArray *mappedArray = [NSMutableArray arrayWithCapacity:array.count];  
    for (NSInteger i = 0; i < array.count; i++) {
        [mappedArray addObject:block(array[i]) ?: [NSNull null]];  
    }  
    _backingArray = [mappedArray copy];

    return self;  
}

- (NSUInteger)count {  
    return self.backingArray.count;  
}
	
- (id)objectAtIndex:(NSUInteger)index {  
    return [self.backingArray objectAtIndex:index];  
}

@end  

Notice here that while the array is backed by a mutable array, it doesn’t expose that in any meaningful way. A default implementation of -map: in a category looks like:

- (NSArray *)map:(id (^)(id object))block {  
    NSMutableArray *mappedArray = [NSMutableArray arrayWithCapacity:self.count];
    	
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        id value = block(obj) ?: [NSNull null];
        [mappedArray addObject:value];
    }];
    
    return mappedArray;  
}  

With an implementation like this, you can forget to call -copy on mappedArray before returning it. It’s easy to forget that -copy, which I know because I’ve done it. If you don’t copy, you’re actually returning an array that can be accidentally cast to a mutable array. With MappedArray that can never happen, because the mutation methods aren’t even implemented.

The class cluster way is definitely more code, but it’s more manipulable than the simple category. It can be lazily-loaded (Gist with sample implementation) if you need those performance characteristics. Lazy loading can never be done with the category way. And if you prefer your interface to be a method on NSArray, you can still use the class cluster method from within a category:

- (NSArray *)map:(id (^)(id object))block {  
	return [MappedArray alloc] initWithArray:self transformationBlock:block];  
}  

The last use for this technique that I want to cover is domain-aware arrays and collections. Because each class cluster is simultaneously an NSArray and whatever its subclassed type is, these arrays can be used in both contexts.

If you had a payroll application, you could create an EmployeesArray class that acts as NSArray. In the same way that -count is a useful method for all arrays, your EmployeesArray can have methods named -totalSalary or -highestPaidEmployee. While you could use KVC collection operators or -reduce to the same end (and perhaps it would be implemented with one of those under the hood), this technique provides you with well-named, well-typed messages. It allows you to be more expressive and clear in your code, and that’s always awesome.

Class clusters are an awesome and underused Cocoa feature. These few techniques can help you realize performance and expressiveness gains at the same time.