# How to Implement the MulleObjCSingleton Protocol in Mulle-ObjC

> Implement the MulleObjCSingleton protocol in Mulle-ObjC for thread-safe shared instances. Gain automatic lifecycle management and hidden storage without boilerplate code.

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

---

**Adopt the `<MulleObjCSingleton>` protocol in your class interface to automatically receive a thread-safe `+sharedInstance` method, hidden storage, and lifecycle management without writing boilerplate synchronization code.**

The MulleObjCSingleton protocol in the mulle-objc/mulleobjc repository provides a production-ready singleton pattern that eliminates manual thread-safety concerns. When you implement the MulleObjCSingleton protocol, the runtime automatically handles instance creation, storage, and cleanup through specialized infrastructure hooks defined in `src/protocol/MulleObjCSingleton.m`.

## How the MulleObjCSingleton Protocol Works

The protocol operates through a coordinated sequence of runtime interactions. During class initialization, the runtime marks your class with the internal `MULLE_OBJC_INFRA_IS_SINGLETON` flag, enabling specialized allocation pathways that bypass standard instance creation rules.

### Protocol Adoption and Class Registration

Declare the protocol in your interface file. This imports the required method signatures from [`src/protocol/MulleObjCSingleton.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/MulleObjCSingleton.h) and signals the runtime to inject singleton behavior during `+initialize`.

The implementation calls `MulleObjCSingletonMarkClassAsSingleton(self)` inside `+initialize` (lines 73-81 in MulleObjCSingleton.m), setting the singleton bit that the allocator checks during subsequent `+sharedInstance` calls.

### Instance Creation and Constantification

When code invokes `+sharedInstance`, the runtime executes `MulleObjCSingletonCreate(Class)` (lines 93-130). This function either retrieves an existing entry from the per-universe concurrent hashmap `_ephemeralSingletonInstances` or allocates a fresh instance via `MulleObjCSingletonNew(self)`.

The `MulleObjCSingletonNew` function (lines 35-80) allocates the object using the standard allocator, then invokes `__initSingleton` if defined; otherwise it falls back to `-init`. The resulting object is immediately **constantified**, making the runtime treat it as immutable and safe for cross-thread sharing without locks.

## Implementing the Protocol in Your Class

Follow these implementation rules to ensure correct behavior within the mulle-objc runtime.

### 1. Declare Protocol Adoption

Add `<MulleObjCSingleton>` to your class interface. This minimal declaration triggers the entire infrastructure.

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

@interface MyManager : NSObject <MulleObjCSingleton>
@end

```

### 2. Implement Optional Initialization

Provide `__initSingleton` only if you require one-time setup logic. The protocol automatically forwards `+sharedInstance` calls to this method during first access.

```objc
@implementation MyManager

- (id)__initSingleton
{
    self = [super init];
    if (self)
    {
        // Perform expensive one-time configuration here
    }
    return self;
}

@end

```

If you omit `__initSingleton`, the protocol uses the standard `-init` method.

### 3. Access the Singleton

Retrieve the instance through the automatically provided class method:

```objc
MyManager *mgr = [MyManager sharedInstance];
NSLog(@"Singleton verified: %s", MulleObjCInstanceIsSingleton(mgr) ? "YES" : "NO");

```

The helper `MulleObjCInstanceIsSingleton(id)` (defined in MulleObjCSingleton.h, lines 62-71) checks the internal infra-class flag to verify singleton status.

## Static vs. Ephemeral Singleton Modes

The protocol supports two storage models controlled by the `MULLE_OBJC_EPHEMERAL_SINGLETON` environment variable.

**Static singletons** (default) live for the entire process lifetime. The instance remains constantified until program termination, with no cleanup overhead.

**Ephemeral singletons** activate when `MULLE_OBJC_EPHEMERAL_SINGLETON=YES` is set. In this mode, the runtime creates a separate instance per universe using the `_ephemeralSingletonInstances` concurrent hashmap (lines 102-115). This isolates state between test runs or thread-local contexts.

Configure ephemeral mode for unit testing:

```sh
export MULLE_OBJC_EPHEMERAL_SINGLETON=YES
./test_runner

```

## Subclassing Considerations

When subclassing a singleton-conforming class, the child inherits the `+sharedInstance` implementation without additional declarations.

```objc
@interface BaseService : NSObject <MulleObjCSingleton>
@end

@implementation BaseService
@end

@interface SubService : BaseService
@end

@implementation SubService
/* Inherits sharedInstance behavior from BaseService */
@end

```

Calling `[SubService sharedInstance]` returns a distinct singleton instance specific to the subclass. Do not redeclare the protocol in subclasses, as this would violate the singleton inheritance chain.

## Critical Implementation Rules

Adherence to these constraints prevents runtime conflicts:

- **Never override `+alloc`** in singleton classes. The infrastructure expects all allocation to occur exclusively within `MulleObjCSingletonNew`.
- **Do not call `[super initialize]`** unless inheriting from another singleton-conforming class. The protocol's `+initialize` implementation (lines 73-81) already handles required setup.
- **Avoid manual instance storage**. The runtime manages the `sharedInstance` storage pointer internally; direct assignment bypasses thread-safety checks.

For reference examples, examine `test/MulleObjCSingleton/singleton.m` in the repository, which demonstrates correct usage patterns and common pitfalls.

## Summary

- Adopt `<MulleObjCSingleton>` in your interface to enable automatic singleton behavior in the mulle-objc runtime.
- The runtime marks classes via `MulleObjCSingletonMarkClassAsSingleton` during `+initialize`, setting the `MULLE_OBJC_INFRA_IS_SINGLETON` flag.
- Use `__initSingleton` for custom one-time setup; otherwise `-init` serves as the default initializer.
- Access instances through `+sharedInstance`, which routes through `MulleObjCSingletonCreate` for thread-safe retrieval.
- Choose between static (process-lifetime) and ephemeral (per-universe) modes using the `MULLE_OBJC_EPHEMERAL_SINGLETON` environment variable.
- Subclasses inherit singleton behavior without protocol redeclaration.

## Frequently Asked Questions

### Can I use MulleObjCSingleton with standard Apple Objective-C or GNUstep?

No, the protocol requires the Mulle-ObjC runtime infrastructure. The `MulleObjCSingletonMarkClassAsSingleton` function and constantification process depend on mulle-objc specific meta-object structures and the `_ephemeralSingletonInstances` hashmap not present in other Objective-C implementations.

### What happens if I accidentally override +alloc in a singleton class?

Overriding `+alloc` breaks the singleton contract expected by `MulleObjCSingletonNew` in `src/protocol/MulleObjCSingleton.m`. The runtime may create multiple instances or fail to constantify the object properly, resulting in non-thread-safe behavior and potential memory leaks during universe cleanup at lines 124-132.

### How do I test code that uses singletons without shared state leaking between tests?

Set the `MULLE_OBJC_EPHEMERAL_SINGLETON` environment variable to `YES` before launching your test harness. This configures the runtime to use the per-universe concurrent hashmap (lines 102-115), creating fresh singleton instances for each universe and preventing cross-test contamination.

### Is the +sharedInstance method thread-safe?

Yes. The implementation uses atomic operations within `MulleObjCSingletonCreate` to ensure that exactly one instance is created even when multiple threads simultaneously invoke `+sharedInstance` for the first time. The constantification process further ensures that the resulting object is safely shareable across all threads without additional synchronization.