Implementing an Objective-C runtime from scratch
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:
|
|
You can write your code like this, linking to the runtime library:
|
|
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:
- nil Receiver handling: Messages sent to
nil
return silently instead of causing segmentation faults; - Category method injection: You can extend existing classes with new methods at compile time by creating categories, allowing the runtime to find and dispatch to these additions seamlessly;
- Runtime introspection: Code can dynamically check whether an object responds to a specific message before attempting to send it, enabling flexible and robust designs.
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:
- Cross-platform compatibility with Clang and GCC compilers
- Zero dynamic memory allocation (using static arrays instead) except for
[Object alloc]
- Full support for Classes, Categories, and Protocols
- Complete test suite for validation
Known limitations:
- No optimized lookup caching for
objc_msg_lookup
yet - Not thread-safe in current implementation
- Surely will run out of static arrays for larger programs!
The runtime requires a small sys
library to abstract platform-specific operations:
sys_puts
for console outputsys_abort
for program terminationsys_sleep
for thread suspensionsys_malloc
andsys_free
for memory managementsys_mutex_lock
andsys_mutex_unlock
for synchronization
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.