Objective-C Method Swizzling example not working

3k views Asked by At

I wanted to try the Method Swizzling to understand clearly how it works. Looking at this code: http://nshipster.com/method-swizzling/. I've created a class and a category, here is the code

#import <Foundation/Foundation.h>

@interface CustomClass : NSObject

-(void) originalMethod;

@end

Implementation of the class

#import "CustomClass.h"

@implementation CustomClass

-(id) init {
    self = [super init];
    return self;
}

-(void) originalMethod {
    NSLog(@"I'm the original method");
}

@end

Category Header:

#import "CustomClass.h"

@interface CustomClass (CustomCategory)

-(void) newMethod;

@end

Category implementation

#import "CustomClass+CustomCategory.h"
#include <objc/runtime.h>

@implementation CustomClass (CustomCategory)

-(void) newMethod {
    [self newMethod];
    NSLog(@"I'm the new method");
}

+(void) load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);

        SEL originalSelector = @selector(originalMethod:);
        SEL swizzledSelector = @selector(newMethod:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class,
                                            originalSelector,
                                            method_getImplementation(swizzledMethod),
                                            method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

@end

and here is the main:

#import <Foundation/Foundation.h>
#import "CustomClass.h"
#import "CustomClass+CustomCategory.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CustomClass *cc = [[CustomClass alloc] init];
        [cc newMethod];
    }
    return 0;
}

when I call [cc newMethod], I get and infinite loop, which shouldn't happen according to the article I linked. I can't see the error in this code.

4

There are 4 answers

0
AR89 On BEST ANSWER

The problem was in the syntax, instead of

SEL originalSelector = @selector(originalMethod:);
SEL swizzledSelector = @selector(newMethod:);

I should have written:

SEL originalSelector = @selector(originalMethod);
SEL swizzledSelector = @selector(newMethod);

xCode gave me just a warning, so I didn't think the selectors names were wrong.

0
bbum On
    class_replaceMethod(class,
                        swizzledSelector,
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));

Shouldn't method_getImplementation(originalMethod) be swizzledMethod?

0
Sash Zats On

It seems like you swizzling class method:

Class class = [self class];

points to a metaclass in +load context, try to specify it explicitly, i.e.

Class class = [CustomClass class];
// or
Class class = self;

Plus, what @bbum said

0
IOSfighter LB On
  1. Wrong syntax, runtime will send different message.

    SEL originalSelector = @selector(originalMethod);
    SEL swizzledSelector = @selector(newMethod);
    
  2. If the first way has no effect. Try it:

    BOOL didAddMethod =
    class_addMethod(class, originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    
    }
    

Exchange the implementations may effect.