Why does ARC call EXC_BAD_ACCESS when executing swizzling functions using class_replaceMethod from the objc runtime library?
I need to replace some implementation methods of concrete Objective-C classes. A set of functions from the objc / runtime library is capable of doing this. To simplify this problem, I'll just write the simplest example code:
#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'll just replace the original implementation of [MyClass func]. When the 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 the problem occurs when ARC is enabled (by setting the compiler flag as -fobjc-arc). EXC_BAD_ACCESS signal is called when gOriginalFunc is called (as the comments say). I don't know the reason, can anyone tell?
source to share
Your function prototype is imprecise and the compiler has misconfigured the call. With ARC, which includes reference counting operations. Any behavior is undefined.
To fix the program, tell the compiler about the valid function signature so that it doesn't mess up the call:
typedef void (*HookFunc_Signature)(id, SEL); // << != IMP signature
static HookFunc_Signature gOriginalFunc = nil;
... later:
gOriginalFunc = (HookFunc_Signature)class_getMethodImplementation(clsMyClass, @selector(func));
source to share