# MulleObjCRuntimeObject Protocol: Requirements and Purpose in MulleObjC

> Discover the MulleObjCRuntimeObject protocol requirements for MulleObjC objects. Understand its purpose in managing lifecycle, thread-safety, and object bridging for optimal runtime performance.

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

---

**The `MulleObjCRuntimeObject` protocol defines the mandatory lifecycle and thread-safety contract that every object managed by the MulleObjC runtime must implement, encompassing retain-release semantics, Thread-Affinity Object (TAO) strategies, and root-object bridging capabilities.**

The `MulleObjCRuntimeObject` protocol serves as the foundational interface in the [mulle-objc/mulleobjc](https://github.com/mulle-objc/mulleobjc) repository, governing how objects interact with the runtime for memory management and concurrent access. Defined in [`src/protocol/MulleObjCRuntimeObject.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/MulleObjCRuntimeObject.h), this protocol abstracts the fundamental requirements for reference counting, thread validation, and object-graph handling that the runtime depends on to manage object lifecycles safely across multiple threads.

## Core Purpose of the MulleObjCRuntimeObject Protocol

The protocol fulfills four critical functions within the MulleObjC runtime architecture:

**Unified Object Lifecycle** — It guarantees that every object can respond to `retain`, `release`, and `retainCount` messages, enabling the runtime’s reference-counting garbage collection system to track object lifetimes consistently.

**Thread-Safety Integration** — The protocol supplies methods for validating and managing safe access across threads, which is essential for MulleObjC’s **Thread-Affinity Object (TAO)** strategy. Methods like `mulleIsThreadSafe` and `mulleIsAccessibleByThread:` allow the runtime to verify that cross-thread object transfers occur safely.

**Object-Graph Support** — The protocol provides the `_becomeRootObject` hook, which the runtime invokes when an object becomes the root of an object graph. This allows special handling during serialization, garbage collection cycles, or graph traversal operations.

**Debug and Validation** — During development, methods such as `mulleIsAccessible` and `mulleIsThreadSafe` enable both developers and runtime assertions to verify correct usage patterns and catch threading violations early.

## Required Methods and Thread-Safety Contract

As specified in [`src/protocol/MulleObjCRuntimeObject.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/MulleObjCRuntimeObject.h), adopting classes must implement the following methods to satisfy the runtime’s core requirements:

**Memory Management Primitives** — The protocol mandates `retain`, `release`, and `retainCount` methods, all marked with the `MULLE_OBJC_THREADSAFE_METHOD` annotation. This annotation expands to `__attribute__((annotate("objc_user_4")))`, informing the compiler and static analysis tools that these implementations must be safe for concurrent invocation.

**Lifecycle Termination** — Classes must implement `dealloc` for instance destruction when the retain count reaches zero. The protocol also declares `finalize` as a legacy finalizer hook, though it remains unused in Automatic Reference Counting (ARC) environments.

**Root Object Bridging** — The `_becomeRootObject` method serves as a callback when the runtime identifies an object as the root of a specific object graph, enabling specialized handling for persistence or garbage collection roots.

**Thread-Affinity Control** — The protocol requires `mulleIsThreadSafe` to return a `BOOL` indicating whether the object permits access from arbitrary threads. For per-thread validation, `mulleIsAccessibleByThread:` accepts an `NSThread` parameter to check accessibility against specific execution contexts.

**Access Transfer Protocol** — When transferring objects between threads, the runtime calls `mulleGainAccess` on the receiving thread and `mulleRelinquishAccess` on the sending thread. Variants such as `mulleGainAccessWithTAOStrategy:` and `mulleRelinquishAccessWithTAOStrategy:` allow explicit specification of transfer strategies. For batch operations, `mulleGainAccessWithUniquingSet:` and `mulleRelinquishAccessWithUniquingSet:` accept a `struct mulle_pointerset *` parameter to prevent duplicate transfers within the same collection.

**Strategy Introspection** — Implementers must provide `mulleTAOStrategy`, which returns a `MulleObjCTAOStrategy` value indicating the object’s current thread-affinity configuration.

## Thread-Affinity Object (TAO) Strategies

The `MulleObjCTAOStrategy` enumeration, defined alongside the protocol, specifies eight distinct strategies controlling how objects transfer between threads. These range from `MulleObjCTAOKnownThreadSafe` (indicating the object requires no special handling) to aggressive cleanup variants like `MulleObjCTAOCallerRemovesFromAllPools`.

When implementing `mulleGainAccessWithTAOStrategy:` or `mulleRelinquishAccessWithTAOStrategy:`, classes select the appropriate strategy based on their internal synchronization capabilities. Thread-safe immutable objects typically return `MulleObjCTAOKnownThreadSafe` from `mulleTAOStrategy`, while mutable objects bound to specific threads use strategies that ensure proper pool management during transfers.

## Practical Implementation Examples

### Minimal Thread-Unsafe Object Implementation

The following implementation in [`src/class/MulleObject.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/class/MulleObject.h) demonstrates the basic contract for a non-thread-safe object:

```objc
#import "MulleObjCRuntimeObject.h"
#import "MulleObjCThreadSafe.h"

@interface SimpleObject : NSObject <MulleObjCRuntimeObject>
{
    NSUInteger _retainCount;
}
@end

@implementation SimpleObject

- (instancetype)retain   MULLE_OBJC_THREADSAFE_METHOD 
{ 
    _retainCount++; 
    return self; 
}

- (void)release        MULLE_OBJC_THREADSAFE_METHOD 
{ 
    if (--_retainCount == 0) 
        [self dealloc]; 
}

- (NSUInteger)retainCount MULLE_OBJC_THREADSAFE_METHOD 
{ 
    return _retainCount; 
}

- (void)dealloc 
{ 
    NSLog(@"SimpleObject dealloc"); 
    [super dealloc]; 
}

- (BOOL)mulleIsThreadSafe MULLE_OBJC_THREADSAFE_METHOD 
{ 
    return NO; 
}

- (void)mulleGainAccess MULLE_OBJC_THREADSAFE_METHOD
{
    // Default behavior for thread-unsafe objects
}

- (void)mulleRelinquishAccess MULLE_OBJC_THREADSAFE_METHOD
{
    // Retain for the receiving thread
    [self retain];
}

@end

```

### Thread-Safe Object with Explicit TAO Strategy

Objects that guarantee internal thread safety implement the protocol with optimized strategies:

```objc
@interface SafeObject : NSObject <MulleObjCRuntimeObject>
@end

@implementation SafeObject

- (instancetype)retain   MULLE_OBJC_THREADSAFE_METHOD 
{ 
    return [super retain]; 
}

- (void)release        MULLE_OBJC_THREADSAFE_METHOD 
{ 
    [super release]; 
}

- (NSUInteger)retainCount MULLE_OBJC_THREADSAFE_METHOD 
{ 
    return [super retainCount]; 
}

- (BOOL)mulleIsThreadSafe MULLE_OBJC_THREADSAFE_METHOD 
{ 
    return YES; 
}

- (MulleObjCTAOStrategy)mulleTAOStrategy MULLE_OBJC_THREADSAFE_METHOD
{
    return MulleObjCTAOKnownThreadSafe;
}

@end

```

### Cross-Thread Object Transfer

The runtime uses the access transfer methods when moving objects between threads:

```objc
// Thread A (Sender)
SimpleObject *obj = [SimpleObject new];
[obj mulleRelinquishAccessWithTAOStrategy:MulleObjCTAOCallerRemovesFromCurrentPool];

// Object passed to Thread B via queue or message

// Thread B (Receiver)
[obj mulleGainAccessWithTAOStrategy:MulleObjCTAOCallerRemovesFromCurrentPool];
// Object now safe to use in Thread B

```

## Summary

- The `MulleObjCRuntimeObject` protocol in [`src/protocol/MulleObjCRuntimeObject.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/protocol/MulleObjCRuntimeObject.h) defines the mandatory interface between objects and the MulleObjC runtime.
- All implementations must provide thread-safe `retain`, `release`, and `retainCount` methods annotated with `MULLE_OBJC_THREADSAFE_METHOD`.
-The protocol enables the Thread-Affinity Object (TAO) system through `mulleGainAccess` and `mulleRelinquishAccess` method pairs.
- Objects declare their thread-safety capabilities via `mulleIsThreadSafe` and `mulleTAOStrategy`, allowing the runtime to optimize cross-thread transfers.
- The `_becomeRootObject` hook provides integration points for garbage collection and object-graph management.

## Frequently Asked Questions

### What is the difference between `mulleGainAccess` and `mulleRelinquishAccess`?

`mulleRelinquishAccess` is invoked on the sending thread before an object transfers to another thread, allowing the object to prepare for the handoff—typically by retaining itself or removing itself from thread-local autorelease pools. `mulleGainAccess` is called on the receiving thread after the transfer completes, enabling the object to re-establish thread-local invariants or add itself to the new thread’s pool management structures.

### When should an object return `YES` from `mulleIsThreadSafe`?

An object should return `YES` only if its internal state is protected by synchronization primitives (such as mutexes or atomic operations) that guarantee safe concurrent access from multiple threads simultaneously. Immutable objects and those using lock-free data structures typically qualify for this designation, while objects with thread-affine mutable state should return `NO`.

### What is the purpose of the `_becomeRootObject` method?

The runtime calls `_becomeRootObject` when an object is identified as the entry point of an object graph, such as during serialization or when establishing garbage collection roots. Implementations can use this hook to perform special initialization, register with external tracking systems, or allocate additional resources required for root-level object management.

### How do I choose the correct `MulleObjCTAOStrategy` for my class?

Select `MulleObjCTAOKnownThreadSafe` if your class guarantees thread safety through internal synchronization. Use `MulleObjCTAOCallerRemovesFromCurrentPool` or similar pool-management strategies for thread-affine objects that require explicit cleanup of autorelease pool memberships during transfers. The strategy returned by `mulleTAOStrategy` must align with the implementation logic in your `mulleGainAccessWithTAOStrategy:` and `mulleRelinquishAccessWithTAOStrategy:` methods.