Objective c 为什么ARC在使用objc运行时库中的class_replaceMethod滑动函数时会导致EXC_BAD_访问?
我需要替换特定Objective-C类的一些方法实现。objc/运行时库中的一组函数能够做到这一点。为了简化问题,我只需编写一个最简单的示例代码,如下所示:Objective c 为什么ARC在使用objc运行时库中的class_replaceMethod滑动函数时会导致EXC_BAD_访问?,objective-c,automatic-ref-counting,hook,objective-c-runtime,method-swizzling,Objective C,Automatic Ref Counting,Hook,Objective C Runtime,Method Swizzling,我需要替换特定Objective-C类的一些方法实现。objc/运行时库中的一组函数能够做到这一点。为了简化问题,我只需编写一个最简单的示例代码,如下所示: #import <Foundation/Foundation.h> #import <objc/runtime.h> @interface MyClass : NSObject - (void) func; @end @implementation MyClass - (void) func { NSL
#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;
}
但问题发生在启用ARC时(通过将编译器标志设置为-fobjc-ARC)。调用gOriginalFunc时抛出EXC_BAD_访问信号(如注释所示)。我不知道原因,有人能告诉我吗?您的函数原型不准确,编译器设置的调用不正确。对于ARC,这包括引用计数操作。两者都是未定义的行为 要更正程序,请将实际的函数签名通知编译器,以便它不会破坏调用:
typedef void (*HookFunc_Signature)(id, SEL); // << != IMP's signature
static HookFunc_Signature gOriginalFunc = nil;
注意:objc术语中的“hook”=“swizzling”非常感谢!这很有帮助。虽然我不太清楚为什么返回类型很重要。我发现这个问题发生在目标SDK版本早于7.0的项目上,因为IMP被定义为:typedef id(*IMP)(id,SEL)。它只在返回类型上与HookFunc_签名不同。@user1224028它取决于ABI和ARC(这可能会引入objc差异)。编译器需要知道如何设置函数调用,否则它将读取和/或写入不应该读取和/或写入的内存,并可能以不同的方式对其进行解释。ARC增加了复杂性,因为调用还可能导致编译器引入保留/释放调用。例如,编译器可能会告诉运行时在存储返回值(例如寄存器值)的位置执行引用计数操作。对于void函数,这将是一个垃圾值(=作为对象处理的未定义行为)。我建议您阅读(尤其是第二个脚注)。它神奇地为我修复了这个EXC_坏访问,我能理解为什么!基本上,编译器(和ARC)倾向于保留swizzled方法的结果,当这些方法不返回NSObject实例时,会导致崩溃。正确地强制转换这些函数的结果可以防止编译器保留太多
typedef void (*HookFunc_Signature)(id, SEL); // << != IMP's signature
static HookFunc_Signature gOriginalFunc = nil;
gOriginalFunc = (HookFunc_Signature)class_getMethodImplementation(clsMyClass, @selector(func));