Objective c 如何实现方法swizzling?

Objective c 如何实现方法swizzling?,objective-c,cocoa,methods,swizzling,simbl,Objective C,Cocoa,Methods,Swizzling,Simbl,我正在尝试使用SIMBL修改程序的行为(我没有它的源代码)。我使用了类转储,发现我需要覆盖一个实例方法 此方法位于名为controller的类中。我需要做的就是得到参数arg1,就这样。可能会记录它或发布通知。。。 我在objective-c中读到了有关方法swizzling的内容,但我如何使用它呢?。我需要引用MessageController类,我没有它的课程 谢谢 我猜您需要在完成NSLog之后调用原始实现;如果没有,您可以只使用类上的类别来重写该方法 要切换该方法,首先需要一个替换方法。

我正在尝试使用SIMBL修改程序的行为(我没有它的源代码)。我使用了类转储,发现我需要覆盖一个实例方法

此方法位于名为controller的类中。我需要做的就是得到参数arg1,就这样。可能会记录它或发布通知。。。 我在objective-c中读到了有关方法swizzling的内容,但我如何使用它呢?。我需要引用MessageController类,我没有它的课程


谢谢

我猜您需要在完成NSLog之后调用原始实现;如果没有,您可以只使用类上的类别来重写该方法

要切换该方法,首先需要一个替换方法。我通常在目标类的某个类别中放置类似的内容:

- (void)replacementReceiveMessage:(const struct BInstantMessage *)arg1 {
    NSLog(@"arg1 is %@", arg1);
    [self replacementReceiveMessage:arg1];
}
这看起来像是它会递归地调用自己,但不会,因为我们要交换东西,所以调用
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);
    }
}
有两种情况需要处理:

  • 如果我们正在swizzling的方法实际上是在一个超类中定义的,那么我们必须使用
    class\u addMethod
    向目标类添加
    ReceiveMessage:
    的实现,这是我们使用替换实现完成的。然后我们可以使用
    class\u replaceMethod
    用超类的实现替换
    replacementReceiveMessage:
    ,这样我们的新版本就能够正确地调用旧版本
  • 如果该方法是在目标类中定义的,
    class\u addMethod
    将失败,但是我们可以使用
    method\u exchangeimplements
    来交换新版本和旧版本
    • 由库处理。不建议自己动手,因为有很多细节需要处理。(请参阅jrswizzle自述文件中记录以前实现失败的表格。)

      假设你有这样的课:

      @interface Weh : NSObject
      -(void)foo;
      -(void)bar;
      @end
      
      @implementation Weh
      -(void)foo {
          NSLog(@"Foo called");
      }
      -(void)bar {
          NSLog(@"Bar called");
          [self bar];
      }
      @end
      
      Weh *weh = Weh.new;
      [weh foo];
      [Weh jr_swizzleMethod:@selector(foo) withMethod:@selector(bar) error:nil];
      [weh foo]; 
      
      您可以这样使用它:

      @interface Weh : NSObject
      -(void)foo;
      -(void)bar;
      @end
      
      @implementation Weh
      -(void)foo {
          NSLog(@"Foo called");
      }
      -(void)bar {
          NSLog(@"Bar called");
          [self bar];
      }
      @end
      
      Weh *weh = Weh.new;
      [weh foo];
      [Weh jr_swizzleMethod:@selector(foo) withMethod:@selector(bar) error:nil];
      [weh foo]; 
      
      输出:

      Foo called
      Bar called
      Foo called
      

      沿着这条路走下去的是疯狂。。。。不要那样做!看起来像是#import#import的复制品如果他没有源代码,那么实际编译并将此代码注入程序的细节是什么?一个问题……不调用
      [self bar]内部
      -(无效)条形图
      创建一个循环?@编号,因为
      条形图
      的实现(如
      Weh
      中所述)已与
      foo
      的实现进行了交换。在运行时,
      foo
      的实现打印
      Bar
      并调用
      Bar
      ,而
      Bar
      的实现只打印
      foo
      。这就是为什么在swizzle之后,
      [weh foo]
      打印
      然后
      foo
      。如果
      foo
      的原始实现调用了
      foo
      ,那么
      [weh foo]
      将永远循环,打印
      Bar
      然后
      foo
      然后
      Bar
      无限。