# How to Create Class Clusters with the MulleObjCClassCluster Protocol in Mulle-ObjC

> Learn to create class clusters with the MulleObjCClassCluster protocol in Mulle-ObjC. Understand how this protocol intercepts alloc to provide shared placeholder objects and concrete subclass instances during init for efficient...

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

---

**The MulleObjCClassCluster protocol enables class clusters by intercepting `+alloc` to return a shared placeholder object that gets replaced with a concrete subclass instance during `-init`.**

The **MulleObjCClassCluster protocol** provides the foundation for implementing class clusters in the mulle-objc/mulleobjc runtime. This design pattern allows a public class to act as a façade that transparently instantiates optimized private subclasses based on initialization parameters. Understanding this mechanism is essential for building Foundation-like abstractions where interface simplicity hides implementation complexity.

## What the MulleObjCClassCluster Protocol Provides

The protocol and its associated helper functions in [`src/protocol/MulleObjCClassCluster.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/MulleObjCClassCluster.h) and `src/protocol/MulleObjCClassCluster.m` provide the infrastructure for placeholder-based allocation.

### Core Protocol Methods

Classes conforming to the **MulleObjCClassCluster protocol** must implement or inherit three key behaviors:

- **`+ (void)initialize`** — Called once per class hierarchy. It marks the class as a class cluster by invoking `MulleObjCClassMarkAsClassCluster`, which sets the internal flag `MULLE_OBJC_INFRA_IS_CLASSCLUSTER` (implemented at lines 66-74 of `MulleObjCClassCluster.m`).

- **`+ (Class)__classClusterClass`** — Returns the class that should be used to create the placeholder. The default implementation returns `self`, but subclasses can override this to specify a different placeholder class.

- **`- (BOOL)__isClassClusterObject`** — Detects whether an object is the placeholder (a constant-ified object). Implemented at lines 34-38 of `MulleObjCClassCluster.m`, this method checks for the constant object flag.

### Helper Functions

The protocol relies on two C functions exposed in the header:

- **`MulleObjCClassMarkAsClassCluster(Class cls)`** — Sets the class-cluster flag on the class infrastructure.
- **`MulleObjCNewClassClusterPlaceholder(Class cls)`** — Allocates and configures the placeholder object using the universe allocator, optionally invokes `__initClassCluster`, and marks the object as constant via `_mulle_objc_object_constantify_noatomic`.

## How `+alloc` Works with the MulleObjCClassCluster Protocol

The `+alloc` method is overridden in `MulleObjCClassCluster.m` (lines 77-118) to implement the placeholder pattern:

1. **Check the class-cluster flag** (`MULLE_OBJC_INFRA_IS_CLASSCLUSTER`). If the flag is not set, normal allocation via `_MulleObjCClassAllocateInstance` is performed.

2. **If it is a class-cluster**:
   - Look for an existing placeholder stored in the infra-class via `_mulle_objc_infraclass_get_classcluster`.
   - If none exists, create a **new placeholder** by calling `MulleObjCNewClassClusterPlaceholder`. This allocates the object using the universe's allocator, optionally invokes a custom `__initClassCluster` method, and marks the object as constant so it is not deallocated by normal reference-counting.
   - Store the placeholder in the infra-class for future `+alloc` calls.

The placeholder is returned as a retained object. The caller's `-init` implementation should release it, as demonstrated in the test examples.

## Creating a Class Cluster with MulleObjCClassCluster

Implementing a class cluster requires four specific steps:

1. **Declare protocol conformance** — Add `<MulleObjCClassCluster>` to the class interface. This signals to the runtime that this class uses the placeholder allocation pattern.

2. **(Optional) Provide a custom placeholder initializer** — Implement a method named `- (void)__initClassCluster` if the placeholder must perform additional setup work. The placeholder creation code in `MulleObjCNewClassClusterPlaceholder` automatically calls this selector if implemented.

3. **Implement `-init` to swap the placeholder** — In the `-init` method, release the placeholder (`[self release]`) and return a concrete instance, often from a private subclass. This is the critical step that transforms the placeholder into a real object.

4. **Ensure `+initialize` calls `super`** — If the class overrides `+initialize`, it must call `[super initialize]` so the class-cluster flag is set via `MulleObjCClassMarkAsClassCluster`. This is documented in [`MulleObjCClassCluster.h`](https://github.com/mulle-objc/mulleobjc/blob/main/MulleObjCClassCluster.h) at lines 62-66.

## Using the MulleObjCClassCluster Protocol in Practice

The test file `test/MulleObjCClassCluster/cluster.m` demonstrates the complete lifecycle:

```objc
// Declaration
@interface Foo : NSObject <MulleObjCClassCluster>
@end

@implementation Foo
- (id)init
{
    [self release];               // discard the placeholder
    return [Bar new];             // create a real instance
}
@end

```

Usage in client code:

```objc
Foo *foo = [Foo alloc];               // gets placeholder
BOOL isPlaceholder = [foo __isClassClusterObject]; // → YES

foo = [foo init];                     // now a real Bar instance
BOOL isBar = [foo isKindOfClass:[Bar class]]; // → YES

```

The placeholder reports `YES` for `__isClassClusterObject`, while the concrete instance returns `NO` and responds to `isKindOfClass:` appropriately. The test confirms that no memory leaks occur because the placeholder is constant and the concrete instance follows normal retain-count rules.

## Why the MulleObjCClassCluster Protocol Works

The mechanism relies on three runtime features:

- **Constant objects** — Placeholders are marked constant via `_mulle_objc_object_constantify_noatomic`, ensuring they are never deallocated by the reference-counting system and can be safely shared across all `+alloc` calls.

- **Universe allocator** — Placeholders are allocated using the universe allocator rather than the standard instance allocator, preventing them from appearing as memory leaks in test runs.

- **Infra-class storage** — The placeholder is cached in the infra-class structure via `_mulle_objc_infraclass_get_classcluster`, ensuring subsequent `+alloc` calls return the same placeholder object, mimicking the classic Cocoa class-cluster behavior found in `NSString` and `NSArray`.

## Summary

- The **MulleObjCClassCluster protocol** intercepts `+alloc` to return a shared placeholder object stored in the infra-class.
- Placeholders are **constant objects** that bypass reference counting and are cached for reuse across all allocation calls.
- Concrete instances are created during `-init` by releasing the placeholder and returning a subclass instance.
- Implementation requires declaring protocol conformance, overriding `-init` to swap objects, and ensuring `+initialize` propagates to super.

## Frequently Asked Questions

### What is the difference between a class cluster and a normal class?

A normal class allocates and initializes a single concrete type when you call `[Class alloc] init]`. A **class cluster** uses the **MulleObjCClassCluster protocol** to return a lightweight placeholder on `+alloc`, then substitutes a concrete subclass instance during `-init` based on the initialization parameters. This allows the public API to remain simple while the implementation can choose optimized private subclasses.

### Why does `+alloc` return a placeholder instead of a full instance?

The placeholder pattern allows the class to defer the actual instance creation until `-init` receives the initialization arguments. In `src/protocol/MulleObjCClassCluster.m`, the overridden `+alloc` checks for the `MULLE_OBJC_INFRA_IS_CLASSCLUSTER` flag and returns a cached placeholder object. This placeholder is a **constant object** that serves as a temporary vessel until the real subclass instance is created and returned by `-init`.

### How do I prevent memory leaks when implementing `-init`?

You must explicitly release the placeholder before returning the concrete instance. In your `-init` implementation, call `[self release]` to discard the placeholder object, then return the new instance created from your concrete subclass. The test file `test/MulleObjCClassCluster/cluster.m` demonstrates this pattern at lines 18-22, ensuring the placeholder's constant status doesn't interfere with normal retain counts for the concrete object.

### Can I use MulleObjCClassCluster with ARC?

The **MulleObjCClassCluster protocol** is designed for manual retain-count management as implemented in the mulle-objc runtime. The placeholder mechanism relies on explicit `release` calls in `-init` and the use of constant objects that bypass normal reference counting. While ARC (Automatic Reference Counting) is not typically used with the mulle-objc runtime, if you were to adapt this pattern to ARC environments, you would need to bridge the placeholder release using `CFRelease` or similar mechanisms to avoid retain cycles.