# Understanding the Infraclass and Metaclass Hierarchy in mulle-objc-runtime

> Explore the infraclass and metaclass hierarchy in mulle-objc-runtime. Understand how three C structures create parallel inheritance chains for instance and class behavior.

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

---

**The infraclass and metaclass hierarchy in mulle-objc-runtime consists of three C structures—`struct _mulle_objc_infraclass` for instance layouts, `struct _mulle_objc_class` as the common base, and `struct _mulle_objc_metaclass` for class methods—wired together to create parallel inheritance chains for instance and class behavior.**

The `mulle-objc-runtime` reimplements Objective-C's object model using pure C structures while maintaining full fidelity to the traditional runtime semantics. Understanding the infraclass and metaclass hierarchy is essential for anyone extending the runtime, debugging class loading, or implementing low-level Objective-C tooling.

## Core Architecture of the Infraclass and Metaclass Hierarchy

The runtime models every logical class as a pair of objects: the **infraclass** handling instance state and the **metaclass** handling class behavior. Both share a common base structure but serve distinct roles in the object model.

### The Three Core Structures

Three tightly-coupled C structures define the hierarchy:

- **`struct _mulle_objc_infraclass`** — Represents the *instance* side of a class. It stores object layout, ivars, properties, and instance method lists. Every concrete, instantiable class has an infraclass, and it embeds a `struct _mulle_objc_class` as its base.

- **`struct _mulle_objc_class`** — Serves as the abstract base holding runtime information common to both infraclasses and metaclasses. This includes the class ID, name pointer, superclass reference, method caches, flags, and a pointer to the corresponding metaclass. Defined in [`src/mulle-objc-class.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-class.h).

- **`struct _mulle_objc_metaclass`** — Represents the *class object* itself, containing class methods and class variables. It also inherits from `struct _mulle_objc_class`, but its superclass chain parallels the infraclass inheritance chain.

### How the Hierarchy is Wired

The wiring between these structures creates Objective-C's characteristic parallel inheritance system:

```

infraclass (Foo) ──► struct _mulle_objc_infraclass
   │                                 │
   │ inherits (base)                 │ base = struct _mulle_objc_class
   ▼                                 ▼
struct _mulle_objc_class (Foo class object)
   │                                 │
   │ has → metaclass pointer ──► struct _mulle_objc_metaclass (Foo's metaclass)
   │                                 │
   │ inherits (base)                 │ base = struct _mulle_objc_class
   ▼                                 ▼
struct _mulle_objc_class (metaclass of Foo)

```

The infraclass's `base` field contains a standard `struct _mulle_objc_class`. That base structure's `metaclass` pointer references the metaclass for the same logical class. Crucially, the metaclass's superclass points to the metaclass of the infraclass's superclass, creating a parallel chain for class-method inheritance that terminates at the root metaclass.

## Root of the Hierarchy

Every `mulle-objc-runtime` universe establishes a root infraclass (typically corresponding to `NSObject` in standard configurations) and its associated root metaclass. The root infraclass has no superclass, and the root metaclass's superclass pointer is `NULL`, terminating both inheritance chains.

These root objects are created during universe initialization in [`src/mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-universe.c). The function `mulle_objc_universe_initialize` sets `universe->foundation.rootclassid` and registers both the root infraclass and root metaclass in the global class tables.

## Working with Infraclasses and Metaclasses in Code

When creating or introspecting classes programmatically, you interact with these structures through specific initialization patterns and accessor functions.

### Declaring a Class Pair

The following pattern mirrors the runtime's internal class loading mechanism, showing how to explicitly initialize an infraclass and metaclass pair:

```c
/* foo.c – implementation */
#include "mulle-objc-runtime.h"
#include "mulle-objc-infraclass.h"
#include "mulle-objc-metaclass.h"

/* 1. Create the infraclass (instance side) */
static struct _mulle_objc_infraclass  Foo_infra;

/* 2. Create the metaclass (class-object side) */
static struct _mulle_objc_metaclass  Foo_meta;

/* 3. Initialise both structures */
static void Foo_initialize(void)
{
    struct mulle_allocator *allocator = mulle_default_allocator;

    /* initialise the infraclass */
    _mulle_objc_infraclass_plusinit(&Foo_infra, allocator);
    _mulle_objc_class_init(
        _mulle_objc_infraclass_as_class(&Foo_infra),
        "Foo",
        sizeof(struct Foo),
        0,
        mulle_objc_get_classid("Foo"),
        /* superclass = */ &_mulle_objc_universe_get_rootclassid,
        /* universe   = */ &_mulle_objc_universe_get_universe());

    /* initialise the metaclass */
    _mulle_objc_metaclass_plusinit(&Foo_meta, allocator);
    _mulle_objc_class_init(
        _mulle_objc_metaclass_as_class(&Foo_meta),
        "Foo",
        0,
        0,
        mulle_objc_get_classid("Foo"),
        /* superclass = */ _mulle_objc_class_get_metaclass(
                                _mulle_objc_class_get_superclass(
                                   _mulle_objc_infraclass_as_class(&Foo_infra))),
        /* universe   = */ &_mulle_objc_universe_get_universe());

    /* link the two sides together */
    _mulle_objc_class_set_metaclass(_mulle_objc_infraclass_as_class(&Foo_infra),
                                    _mulle_objc_metaclass_as_class(&Foo_meta));
}

```

The code demonstrates the explicit relationship: both the infraclass and metaclass embed a `struct _mulle_objc_class`, accessed via the `*_as_class` casting helpers. The metaclass's superclass is explicitly set to the metaclass of the parent infraclass, maintaining the parallel hierarchy.

### Accessing the Metaclass Chain

To traverse the metaclass inheritance chain from user code:

```c
/* Assume we have a `struct Foo *obj` */
struct _mulle_objc_class *cls = mulle_objc_object_get_class((struct _mulle_objc_object *)obj);

/* Get the metaclass that holds class methods */
struct _mulle_objc_metaclass *meta = (struct _mulle_objc_metaclass *)mulle_objc_class_get_metaclass(cls);

/* Walk the metaclass chain */
while (meta)
{
    printf("Metaclass name: %s\n", mulle_objc_class_get_name(&meta->base));
    meta = (struct _mulle_objc_metaclass *)mulle_objc_class_get_superclass(&meta->base);
}

```

`mulle_objc_class_get_metaclass` retrieves the metaclass associated with any class object, while `mulle_objc_class_get_superclass` walks the inheritance chain—whether for infraclasses or metaclasses—depending on the starting point.

### Type Checking

The runtime provides predicates to distinguish between infraclasses and metaclasses at runtime:

```c
if (mulle_objc_class_is_infraclass(cls))
    printf("This is an infraclass (instance side)\n");
else if (mulle_objc_class_is_metaclass(cls))
    printf("This is a metaclass (class-object side)\n");

```

These functions inspect the `state` bits defined in [`src/mulle-objc-class.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-class.h), allowing safe dispatch based on class type.

## Key Helper Functions and APIs

The runtime exposes several inline accessor functions defined in the header files that abstract the casting and navigation between hierarchy levels:

- **`_mulle_objc_infraclass_as_class(infra)`** — Casts an infraclass pointer to its embedded `struct _mulle_objc_class` base.
- **`_mulle_objc_class_get_metaclass(cls)`** — Retrieves the metaclass associated with a given class or infraclass.
- **`_mulle_objc_class_get_superclass(cls)`** — Walks the infraclass inheritance chain.
- **`_mulle_objc_metaclass_get_superclass(meta)`** — Walks the metaclass inheritance chain, parallel to the corresponding infraclass chain.
- **`mulle_objc_universe_register_infraclass(universe, infra)`** — Registers a newly created infraclass and automatically links its metaclass into the universe's class tables.

These functions are used throughout [`src/mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-universe.c) during class loading, method lookup, and object allocation.

## Summary

- The **infraclass** (`struct _mulle_objc_infraclass`) defines instance layout and instance methods, embedding a base `struct _mulle_objc_class`.
- The **metaclass** (`struct _mulle_objc_metaclass`) defines class methods and also embeds `struct _mulle_objc_class`, inheriting from the metaclass of the parent infraclass.
- The **base class structure** (`struct _mulle_objc_class`) contains the `metaclass` pointer linking the two sides and the `superclass` pointer establishing inheritance.
- Root classes are initialized in [`mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-universe.c), establishing the termination points for both inheritance chains.
- Helper functions like `_mulle_objc_class_get_metaclass` and type predicates abstract the casting required to navigate between these structures.

## Frequently Asked Questions

### What is the difference between an infraclass and a metaclass in mulle-objc?

An **infraclass** (`struct _mulle_objc_infraclass`) represents the template for instance objects, storing ivars, instance methods, and object size. A **metaclass** (`struct _mulle_objc_metaclass`) represents the class object itself, storing class methods and metadata. Every logical class has one of each, linked through the base structure's `metaclass` pointer.

### How does the metaclass inheritance chain work?

The metaclass inheritance chain runs parallel to the infraclass chain. If class `B` inherits from class `A`, then `B`'s metaclass inherits from `A`'s metaclass. This ensures that class methods follow the same inheritance pattern as instance methods. The chain terminates at the root metaclass, whose superclass is `NULL`.

### Where are the root infraclass and metaclass initialized?

The root infraclass and metaclass are created during universe bootstrap in [`src/mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-universe.c). The function `mulle_objc_universe_initialize` sets up `universe->foundation.rootclassid` and registers both structures, establishing the foundation for the entire class hierarchy.

### How do I check if a struct pointer is an infraclass or metaclass?

Use the runtime's type predicate functions: `mulle_objc_class_is_infraclass(cls)` returns true for infraclasses, while `mulle_objc_class_is_metaclass(cls)` returns true for metaclasses. These inspect bit flags in the class's `state` field defined in [`src/mulle-objc-class.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-class.h).