Skip to content
The Objective-C Runtime
step 1/5

Reading — step 1 of 5

Learn

~2 min readMemory, Runtime, Modern OC

Objective-C is dynamically typed — every method dispatch goes through the runtime. This enables features no static language has: method swizzling, class extension at runtime, KVO, dynamic class creation.

The runtime API

Includes <objc/runtime.h>:

#import <objc/runtime.h>

Class cls = [NSString class];
Method m = class_getInstanceMethod(cls, @selector(uppercaseString));
IMP impl = method_getImplementation(m);
%% impl is the actual C function pointer

Method swizzling

Replace a method's implementation at runtime — used by debuggers, mocking libraries, KVO:

@implementation NSString (Logging)
+ (void)load {
    Method original = class_getInstanceMethod([NSString class], @selector(length));
    Method swizzled = class_getInstanceMethod([NSString class], @selector(loggedLength));
    method_exchangeImplementations(original, swizzled);
}
- (NSUInteger)loggedLength {
    NSUInteger len = [self loggedLength];     %% calls the ORIGINAL after swizzle
    NSLog(@"length called: %lu", len);
    return len;
}
@end

Now EVERY call to [someString length] goes through your wrapper. Powerful and dangerous.

Dynamic class creation

Class DynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);
class_addMethod(DynamicClass, @selector(greeting), 
                (IMP)greetingIMP, "@@:");
objc_registerClassPair(DynamicClass);

id instance = [[DynamicClass alloc] init];
[instance performSelector:@selector(greeting)];

Used by ORMs, KVO, mocking frameworks.

Method introspection

%% List all methods of a class
unsigned int count;
Method *methods = class_copyMethodList([NSString class], &count);
for (unsigned int i = 0; i < count; i++) {
    SEL sel = method_getName(methods[i]);
    NSLog(@"method: %s", sel_getName(sel));
}
free(methods);

Useful for serialization, dependency injection, debugging.

Associated objects

Attach extra data to existing instances without subclassing:

static char kAssocKey;

objc_setAssociatedObject(someObject, &kAssocKey, @"extra", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
NSString *extra = objc_getAssociatedObject(someObject, &kAssocKey);

This is how you add stored properties via categories — categories normally can't add ivars, but associated objects work around it.

NSInvocation

For calls with more args than performSelector supports, or for method forwarding:

NSMethodSignature *sig = [target methodSignatureForSelector:@selector(method:arg2:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
[inv setTarget:target];
[inv setSelector:@selector(method:arg2:)];
[inv setArgument:&arg1 atIndex:2];      %% 0=self, 1=_cmd, 2+=user args
[inv setArgument:&arg2 atIndex:3];
[inv invoke];
NSString *result;
[inv getReturnValue:&result];

Verbose but powerful — used in NSProxy, KVO observation forwarding.

Why this matters

Objective-C's dynamism makes:

  • KVO (Key-Value Observing) — observe property changes without writing notification code
  • Cocoa Bindings — auto-sync UI to model
  • Core Data — automatic ORM with NSManagedObject
  • Swift's @objc bridging — Swift can call Objective-C dynamic methods

Understanding the runtime is essential for serious ObjC work — and gives you appreciation for what Swift gives up (and gains) by being statically typed.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…