如何实现方法调配?

use*_*064 9 methods cocoa objective-c simbl swizzling

我试图使用SIMBL修改程序的行为(我没有它的来源).我使用了类转储,发现我需要覆盖一个实例方法

此方法位于名为controller的类中.我需要做的就是获得参数arg1,就是这样.也许NSLog它或发布通知...我读到了Objective-c中的方法调配但我怎么能用它?我需要引用我没有的课程MessageController.

谢谢!

Ano*_*mie 33

我猜你需要在做完NSLog后调用原来的实现; 如果没有,您可以只使用类上的类别来覆盖该方法.

要调整方法,首先需要更换方法.我通常把这样的东西放在目标类的一个类别中:

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

这看起来会以递归方式调用自身,但它不会因为我们要交换东西所以调用ReceiveMessage:调用方法时调用replacementReceiveMessage:旧版本.

第二步是使用运行时函数来实际执行交换.使用类别的优点是您可以load在类别中使用来完成工作:

+ (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);
    }
}
Run Code Online (Sandbox Code Playgroud)

有两种情况需要处理:

  • 如果我们调整的方法实际上是在超类中定义的,我们必须使用class_addMethodReceiveMessage:目标类添加一个实现,我们使用替换实现来实现.然后我们可以使用class_replaceMethod替换replacementReceiveMessage:超类的实现,所以我们的新版本将能够正确调用旧的.
  • 如果在目标类中定义了该方法,class_addMethod则会失败,但我们可以使用它method_exchangeImplementations来交换新旧版本.


Isa*_*sak 6

jrswizzle处理它.不建议您自己动手,因为有很多细节要做.(请参阅jrswizzle自述文件中记录以前实现失败的表格.)

假设你有这样的课程:

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

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

你可以像这样使用它:

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

输出:

Foo called
Bar called
Foo called
Run Code Online (Sandbox Code Playgroud)

  • @TheTiger不,因为在`Weh`中写的`bar`的实现已经与`foo`交换了.在运行时,`foo`的实现打印`Bar`并调用`bar`,而`bar`的实现只打印`Foo`.这就是为什么在混合之后,`[weh foo]`打印`Bar`然后'Foo`.如果`foo`的原始实现调用了`foo`那么`[weh foo]`将永远循环,打印`Bar`然后`Foo`然后打印'Bar`无限. (2认同)