Why does ARC cause EXC_BAD_ACCESS when swizzling functions using class_replaceMethod from objc runtime library?

1k views Asked by At

I need to replace some methods’ implementations of specific Objective-C classes. A set of functions from objc/runtime library is capable of doing that. To simplify the issue I just write a simplest sample code as following:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface MyClass : NSObject
- (void) func;
@end

@implementation MyClass

- (void) func {
    NSLog(@"Hello from MyClass!");
}

@end

//Original implementation of the method
static IMP gOriginalFunc = nil;

//Hook function that will take place of the original method
void HookFunc(id self, SEL _cmd)
{
    NSLog(@"[MyClass func] is hooked!");
    gOriginalFunc(self, _cmd);//EXC_BAD_ACCESS occurs here when ARC is enabled!!
}

int main(int argc, char *argv[])
{
    Class clsMyClass = objc_getClass("MyClass");
    // Restore the original method implementation:
    gOriginalFunc = class_getMethodImplementation(clsMyClass, @selector(func));
    Method mtdFunc = class_getInstanceMethod(clsMyClass, @selector(func));
    // Replace implementaion of the method of my own:
    class_replaceMethod(clsMyClass, @selector(func), IMP(HookFunc), method_getTypeEncoding(mtdFunc));
    objc_registerClassPair(clsMyClass);

    MyClass* obj = [[MyClass alloc] init];
    [obj func];

    return 0;
}

I just replace the original implementation of [MyClass func]. When compiler flag -fno-objc-arc is set, this code works just fine:

2014-12-18 11:59:17.524 Hooker[749:72783] [MyClass func] is hooked!
2014-12-18 11:59:20.361 Hooker[749:72783] Hello from MyClass!

But problem occurs when ARC is enabled(by setting compiler flag as -fobjc-arc). A EXC_BAD_ACCESS signal is thrown while invoking gOriginalFunc(as comments say). I don’t know the reason, could anyone tell?

1

There are 1 answers

3
justin On BEST ANSWER

Your function prototype is inaccurate, and the compiler sets up the call incorrectly. With ARC, that includes reference count operations. Either is undefined behavior.

To correct the program, inform the compiler of the actual function signature so it doesn't screw up the call:

typedef void (*HookFunc_Signature)(id, SEL); // << != IMP's signature
static HookFunc_Signature gOriginalFunc = nil;

...later:

gOriginalFunc = (HookFunc_Signature)class_getMethodImplementation(clsMyClass, @selector(func));