1

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.

Borys Verebskyi
  • 4,160
  • 6
  • 28
  • 42
AR89
  • 3,548
  • 7
  • 31
  • 46
  • Are you sure the load method is being called? – Kegluneq Nov 21 '14 at 13:29
  • @user1963877 no, I've not checked it. It should be called since it's a method present in NSObject. – AR89 Nov 21 '14 at 13:33
  • Here's something about using `+(void)load` [link](https://www.mikeash.com/pyblog/friday-qa-2009-05-22-objective-c-class-loading-and-initialization.html). Notice that your tutorial involves normal CocoaTouch application (with ui thread and such) while you're trying it on basically naked hello world program, so maybe it behaves differently. – Kegluneq Nov 21 '14 at 13:43
  • @user1963877 Won't make a difference. – bbum Nov 21 '14 at 17:14
  • I've checked, +load is called and the code runs into: "if (didAddMethod)" – AR89 Nov 22 '14 at 13:31
  • 2
    I see you found the problem, but there's another thing: it's not wrong, per se, but you're calling `[cc newMethod]` from `main()`. When you swizzle methods, the usual intent is that the new method is a hidden implementation detail. Clients would use the old method and the intent of swizzling is to change the behavior of that. I realize that you're probably just experimenting, but wanted to make sure you understand how it's supposed to work. – Ken Thomases Nov 23 '14 at 00:25

4 Answers4

1

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.

AR89
  • 3,548
  • 7
  • 31
  • 46
0
    class_replaceMethod(class,
                        swizzledSelector,
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));

Shouldn't method_getImplementation(originalMethod) be swizzledMethod?

bbum
  • 162,346
  • 23
  • 271
  • 359
0

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

Sash Zats
  • 5,376
  • 2
  • 28
  • 42
0
  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.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53