Implementing an Objective-C runtime from scratch

July 7, 2025

What motivates someone to adopt one of the ten least desired programming languages in 2025? I’d learnt Objective-C at least two decades ago, whilst developing for a relatively obscure Apple technology called WebObjects.

While most developers recoiled at the endless square brackets, I found myself drawn to its approach to object-oriented design, memory management, and surprisingly readable (if verbose) code.

I’ve wanted to write code for Embedded systems (in a memory-constrained environment) with an event-driven approach. I started with a C approach but the complexity of that, and my concern around resource management on a device without a lot of resources, meant it just didn’t feel quite right yet. Based on my previous experience with Objective-C, I thought it might be good to explore how I could implement a runtime library for Objective-C that would work in this environment. Whilst there are some reasons not to (which I’ll come to later), it’s certainly worth a spike.

The Runtime Library

The magic of Objective-C lies in its runtime library. Unlike C or C++, where method calls are resolved at compile time, Objective-C uses dynamic message dispatch. When you write [Object greet], the compiler translates this into a runtime lookup that finds and executes the appropriate method implementation - whether that be from the Class, Superclass or Mixin (aka, Category)

Both Clang and GCC compilers include built-in support for Objective-C, but they still require a runtime library for you to be able to execute your code. This provides the crucial bridge between your Objective-C Classes, Categories, and Protocols, and the underlying message dispatch system that actually calls your methods.

This runtime library dependency is one reason Objective-C has fallen out of favour. Every method call requires a trip through the dispatcher - also known as objc_msg_send - consuming precious CPU cycles that modern languages often avoid.

As an experiment, I decided to implement my own Objective-C runtime library targeting GCC. The version I chose to reimplement is GNUstep’s libobjc design - a 20-year-old codebase. There’s also a more modern variant that’s a sprightly 15 years old, Apple’s runtime library and others. In my opinion, the former design is suitable to begin with. You don’t need to write any assembly code, and I want to target different embedded hardware platforms.

Core Implementation

The heart of the GCC runtime library consists of data structures for reflection and registration, plus two critical functions:

1
2
3
4
5
6
// Return the implementation of a method based on the receiver
// and the method selector
IMP objc_msg_lookup(id receiver, SEL selector);

// Return the implementation of a superclass method
IMP objc_msg_lookup_super(struct objc_super* super, SEL selector);

You can write your code like this, linking to the runtime library:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@interface Object
+ (void) greet;
@end

@implementation Object
+ (void) greet {
  printf("Hello, world\n");
}
+@end

void main(void) {
  // Send a "greet" method to the Object class
  [Object greet]
}

Dynamic Dispatch

While [Object greet] might look like a simple method call, the runtime lookup system enables powerful features that static languages can’t match:

This dynamic reflection comes built into Objective-C, unlike C and C++, but at the cost of that round-trip lookup for every message call.

Implementation

I’ve created a runtime library called objc-gcc, available on GitHub with comprehensive documentation.

Current features:

Known limitations:

The runtime requires a small sys library to abstract platform-specific operations:

Additional system functions for threads and timers are planned for future releases.

What’s Next

If I write a next post, I’ll explain the memory management (which mirrors that of Apple’s Foundation framework), exploring how Objective-C’s reference counting and autorelease pools work under the hood, and how I’ve implemented them in a resource-constrained environment.

The question isn’t whether Objective-C deserves a place in modern development - that’s unlikely. But understanding how these runtime systems work provides insights into language design, dynamic dispatch, and how this could work for an event-driven embedded hardware platform.