Episode #124

Method Swizzling

16 minutes
Published on June 19, 2014

This video is only available to subscribers. Get access to this video and 572 others.

In this episode we delve into the wonderful Objective-C runtime in order to replace method implementations with our own. Using this technique we can add or change behavior to existing classes, which can be extremely useful for Aspect Oriented Programming (logging/benchmarking), or analytics.

Episode Links

Swizzle Helper

First, we create a quick helper method to make swizzling a bit easier to use...

#import "FKBSwizzler.h"
#import <objc/runtime.h>

@implementation FKBSwizzler

+ (void)swizzleClass:(Class)class selector:(SEL)originalSelector newSelector:(SEL)newSelector {
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method newMethod = class_getInstanceMethod(class, newSelector);

    BOOL methodAdded = class_addMethod(class, originalSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    if (methodAdded) {
        class_addMethod(class, newSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, newMethod);
    }
}

@end

We can use this to swizzle selectors on the same class, which is the majority use case.

Swizzling in Category Methods

The best way to swizzle methods on a class is to create a category on that class. You define the new method (using your prefix) and also implement + load so the swizzle happens early in the app's lifecycle.

#import "Foo+Swizzling.h"
#import "FKBSwizzler.h"

@implementation Foo (Swizzling)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [FKBSwizzler swizzleClass:self
                         selector:@selector(bar)
                      newSelector:@selector(fkb_bar)];
    });
}

- (void)fkb_bar {
    NSLog(@"---- BAR Being called ---");
    [self fkb_bar];
    NSLog(@"---- BAR finished --- ");
}

The benefit here is that this category applies the behavior itself, so it is quite modular. If you decide you don't want this behavior you simply remove the file from your project and the feature goes away.