# How Protocols Work in mulle-objc: PROTOCOL vs SEL Explained

> Understand how mulle-objc PROTOCOLs define interfaces and SELs dispatch methods. Explore the distinct roles of protocols and selectors in this powerful runtime.

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

---

**In mulle-objc, a PROTOCOL is a unique identifier representing a collection of method signatures stored as `struct _mulle_objc_protocol`, while a SEL (selector) is a 32-bit integer (`mulle_objc_methodid_t`) identifying a single method for dispatch—protocols describe interfaces, selectors drive method calls.**

The mulle-objc runtime, implemented in the `mulle-objc/mulle-objc-runtime` repository, treats protocols and selectors as entirely separate identifier systems. While both compile down to unique integers, protocols track interface conformance through **protocol identifiers** (`mulle_objc_protocolid_t`), whereas **selectors** (`SEL`, aliased to `mulle_objc_methodid_t`) resolve to function pointers for message sending. Understanding this architectural split is critical for debugging conformance issues or extending the runtime.

## What Is a Protocol in mulle-objc?

A protocol in mulle-objc is a compile-time contract that defines a set of methods a class must implement. Unlike selectors, which are ephemeral method names used during dispatch, protocols are persistent runtime objects registered in the universe.

### Runtime Representation

When the compiler encounters `@protocol`, it emits a `struct _mulle_objc_protocol` defined in [`src/mulle-objc-protocol.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-protocol.h):

```c
struct _mulle_objc_protocol {
    mulle_objc_protocolid_t protocolid;   // unique id generated from protocol name
    char *name;                           // textual name, e.g., "Baz"
};

```

This structure is stored in the **protocol table** of the universe (`universe->protocoltable`), a hash table populated during startup. The **protocol identifier** is a unique 32-bit integer derived from the protocol's name, distinct from method selectors.

### Class-Pair Storage

Every class that adopts a protocol stores the protocol identifiers in its **class-pair** structure. In [`src/mulle-objc-classpair.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-classpair.h), the union `p_protocolids` holds a sorted array of `mulle_objc_protocolid_t` values:

```c
union _mulle_objc_uniqueidarraypointer_t p_protocolids;   // array of protocolids

```

This array enables O(log n) conformance checks using binary search routines implemented in [`src/mulle-objc-protocol.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-protocol.c).

## What Is a SEL in mulle-objc?

A **selector** (`SEL`) is simply an alias for `mulle_objc_methodid_t`, defined in [`src/mulle-objc-metaabi.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-metaabi.h) as a 32-bit integer. It represents a single method name used to look up an **IMP** (function pointer) in a class's method table.

### Method Dispatch vs Protocol Conformance

Selectors power the message dispatch machinery found in [`src/mulle-objc-call.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-call.h). When you call `[obj baz]`, the compiler emits:

```objc
SEL sel = @selector(baz);

```

The runtime then uses `mulle_objc_method_lookup()` to find the corresponding function pointer. **There is no link** between a `protocolid` and a `methodid`; a protocol merely documents which selectors a conforming class should implement. The selector table and protocol table are separate namespaces.

## How Protocols Are Registered and Looked Up

During runtime initialization, every compiled `struct _mulle_objc_protocol` is hashed into the universe's protocol table. The lookup function in [`src/mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-universe.c) provides direct access:

```c
struct _mulle_objc_protocol *
_mulle_objc_universe_lookup_protocol(struct _mulle_objc_universe *universe,
                                    mulle_objc_protocolid_t protocolid);

```

This function retrieves the protocol structure by its identifier, useful for introspection or dynamic conformance checks.

## Checking Protocol Conformance

To verify if a class adopts a protocol, the runtime inspects the class-pair's protocol array rather than the method tables.

### The Conformance API

The primary API for conformance testing is declared in [`src/mulle-objc-object-convenience.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-object-convenience.h):

```c
int _mulle_objc_infraclass_conformsto_protocolid(struct _mulle_objc_infraclass *cls,
                                                 mulle_objc_protocolid_t pid);

```

This function searches the class-pair's `p_protocolids` array (and superclasses) for the given identifier. In user code, you typically invoke this via the `@protocol` compiler directive:

```c
if (_mulle_objc_infraclass_conformsto_protocolid(cls, @protocol(Baz))) {
    // Class conforms to Baz
}

```

### Protocol Inheritance

Protocols can adopt other protocols. The compiler flattens the inheritance hierarchy into a single sorted array of protocol IDs stored in the adopting class's protocol-class. When checking conformance against `@protocol(Foo)`, the runtime uses `_mulle_objc_protocol_bsearch` in [`src/mulle-objc-protocol.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-protocol.c) to verify that all inherited protocol IDs are present, ensuring O(log n) performance even with deep inheritance chains.

## Practical Code Examples

### Declaring a Protocol and Adopting Class

```objc
// protocol_example.m
#import <mulle-objc-runtime/mulle-objc-runtime.h>

@protocol Baz
- (void) baz;
@end

@interface Foo <Baz>
@end

@implementation Foo
+ (id) new { return mulle_objc_infraclass_alloc_instance(self); }
- (void) dealloc { _mulle_objc_instance_free(self); }
- (void) baz { printf("Baz called\n"); }
@end

```

### Runtime Conformance Check

```c
int main(void)
{
    Foo *foo = [Foo new];
    struct _mulle_objc_infraclass *cls = mulle_objc_object_get_infraclass(foo);
    
    int conforms = _mulle_objc_infraclass_conformsto_protocolid(cls, @protocol(Baz));
    printf("Conforms to Baz? %s\n", conforms ? "YES" : "NO");
    
    [foo dealloc];
    return 0;
}

```

This test demonstrates the separation of concerns: `@protocol(Baz)` resolves to a `mulle_objc_protocolid_t` for the conformance check, while the actual method call `[foo baz]` uses a separate `SEL` lookup.

### Using Selectors for Direct Dispatch

```c
SEL sel = @selector(baz);
IMP imp = mulle_objc_method_lookup(cls, sel);
if (imp) {
    ((void (*)(id, SEL))imp)(foo, sel);
}

```

Here, `sel` is a `mulle_objc_methodid_t`, not a protocol identifier. This lookup bypasses protocol machinery entirely, going straight to the class's method table.

## Summary

- **Protocols** are runtime structures (`struct _mulle_objc_protocol`) identified by unique **protocol IDs** (`mulle_objc_protocolid_t`) stored in the universe's protocol table and class-pair arrays.
- **Selectors** (`SEL` or `mulle_objc_methodid_t`) are 32-bit integers identifying single methods for dispatch, resolved via method tables unrelated to protocol storage.
- **Conformance checks** use `_mulle_objc_infraclass_conformsto_protocolid` to search sorted protocol ID arrays in the class-pair, supporting O(log n) inheritance checks via `_mulle_objc_protocol_bsearch`.
- The implementation spans [`mulle-objc-protocol.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-protocol.h), [`mulle-objc-classpair.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-classpair.h), and [`mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-universe.c) for protocol handling, while [`mulle-objc-metaabi.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-metaabi.h) and [`mulle-objc-call.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-call.h) manage selector dispatch.

## Frequently Asked Questions

### What is the difference between @protocol and @selector in mulle-objc?

**`@protocol`** generates a **protocol identifier** (`mulle_objc_protocolid_t`) used to verify interface conformance through structures stored in the universe's protocol table and class-pair arrays. **`@selector`** generates a **method identifier** (`mulle_objc_methodid_t`) used by the dispatch system in [`mulle-objc-call.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/mulle-objc-call.h) to locate function pointers. They occupy separate namespaces and serve different purposes: protocols describe contracts, selectors enable calls.

### How does protocol inheritance work in mulle-objc?

When a protocol adopts another protocol (e.g., `@protocol Foo <Bar>`), the compiler generates a combined array of all required protocol IDs. This array is stored in the protocol-class's `p_protocolids` field. The runtime uses binary search (`_mulle_objc_protocol_bsearch` in [`src/mulle-objc-protocol.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-protocol.c)) to check for inherited protocol membership, making conformance verification O(log n) regardless of inheritance depth.

### Where are protocols stored in the mulle-objc runtime?

Protocol definitions reside in two locations: the **universe's protocol table** (`universe->protocoltable` in [`src/mulle-objc-universe.c`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-universe.c)) maps protocol IDs to `struct _mulle_objc_protocol` instances, while each **class-pair** ([`src/mulle-objc-classpair.h`](https://github.com/mulle-objc/mulle-objc-runtime/blob/main/src/mulle-objc-classpair.h)) stores a sorted array of adopted protocol IDs in the `p_protocolids` union. This dual storage enables both global protocol lookup and fast per-class conformance checks.

### Can you convert a SEL to a protocol ID in mulle-objc?

**No.** `SEL` (`mulle_objc_methodid_t`) and `mulle_objc_protocolid_t` are distinct 32-bit identifier types with separate hash spaces and lookup tables. A selector identifies a single method for the dispatch engine, while a protocol ID identifies a collection of method signatures. There is no runtime API to convert between them because they represent orthogonal concepts—method implementation lookup versus interface contract verification.