How do I implement method swizzling?
Asked Answered
B

2

9

I am trying to modify behaviour of a program (i dont have it's source) using SIMBL. I used class dump and found out that I need to overide an instance method

This method is in the class called controller. All I need to do is get the argument arg1 and thats it. Maybe NSLog it or post a notification... I read about method swizzling in objective-c but how can I use it?. I would need to refer to the class MessageController whose course i don't have.

Thanks!

Benford answered 20/3, 2011 at 21:23 Comment(2)
Down this path lies madness.... don't do that!Cupric
Looks like a duplicate of https://mcmap.net/q/1169794/-simbl-with-method-swizzlingHierocracy
F
35

I'm guessing you need to call the original implementation after doing your NSLog; if not, you may be able to just use a category on the class to override the method.

To swizzle the method, first you need a replacement method. I usually put something like this in a category on the target class:

- (void)replacementReceiveMessage:(const struct BInstantMessage *)arg1 {
    NSLog(@"arg1 is %@", arg1);
    [self replacementReceiveMessage:arg1];
}

This looks like it will recursively call itself, but it won't because we're going to swap things around so calling ReceiveMessage: calls this method while calling replacementReceiveMessage: calls the old version.

The second step is to use the runtime functions to actually perform the swap. The advantage of using a category is that you can use load in the category to do the work:

+ (void)load {
    SEL originalSelector = @selector(ReceiveMessage:);
    SEL overrideSelector = @selector(replacementReceiveMessage:);
    Method originalMethod = class_getInstanceMethod(self, originalSelector);
    Method overrideMethod = class_getInstanceMethod(self, overrideSelector);
    if (class_addMethod(self, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
            class_replaceMethod(self, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
            method_exchangeImplementations(originalMethod, overrideMethod);
    }
}

There are two cases that need to be handled:

  • If the method we're swizzling is actually defined in a superclass, we have to use class_addMethod to add an implementation of ReceiveMessage: to the target class, which we do using our replacement implementation. Then we can use class_replaceMethod to replace replacementReceiveMessage: with the superclass's implementation, so our new version will be able to correctly call the old.
  • If the method is defined in the target class, class_addMethod will fail but then we can use method_exchangeImplementations to just swap the new and old versions.
Famish answered 20/3, 2011 at 22:38 Comment(2)
#import <objc/runtime.h> #import <objc/message.h>Preamble
If he doesn't have the source code, what are the details to actually compile and inject this code into the program ?Chilon
D
6

The library jrswizzle handles it. Doing it yourself is not recommended, because there are a lot of details to get right. (See the table documenting the failures of previous implementations in the jrswizzle readme.)

Say you have class like this:

@interface Weh : NSObject
-(void)foo;
-(void)bar;
@end

@implementation Weh
-(void)foo {
    NSLog(@"Foo called");
}
-(void)bar {
    NSLog(@"Bar called");
    [self bar];
}
@end

You can use it like this:

Weh *weh = Weh.new;
[weh foo];
[Weh jr_swizzleMethod:@selector(foo) withMethod:@selector(bar) error:nil];
[weh foo]; 

Output:

Foo called
Bar called
Foo called
Dampproof answered 2/7, 2013 at 19:42 Comment(2)
One question ....Would not calling [self bar]; inside -(void)bar make a cycle?Osswald
@Osswald No, because the implementation of bar, as written in Weh, has been swapped with that of foo. At runtime, foo's implementation prints Bar and calls bar, while bar's implementation simply prints Foo. That is why after the swizzle, [weh foo] prints Bar then Foo. If foo's original implementation had called foo then [weh foo] would loop forever, printing Bar then Foo then Bar ad infinitum.Tableland

© 2022 - 2024 — McMap. All rights reserved.