无法调配类方法

Posted

技术标签:

【中文标题】无法调配类方法【英文标题】:Can't swizzle class methods 【发布时间】:2010-12-08 02:06:14 【问题描述】:

我正在为 Mail.app 制作一个插件。我希望插件在邮件的工具栏上添加一个按钮。为此,我认为最好的方法是调用函数以在 MessageViewer 的初始化方法中添加此按钮(MessageViewer 是 Mail.app 的 FirstResponder 的类)。我正在修改的代码似乎运行良好:

+ (void) initialize
  
[super initialize];

//  class_setSuperclass([self class], NSClassFromString(@"MVMailBundle"));
//  [ArchiveMailBundle registerBundle];


// Add a couple methods to the MessageViewer class.
Class MessageViewer = NSClassFromString(@"MessageViewer");

// swizzleSuccess should be NO if any of the following three calls fail
BOOL swizzleSuccess = YES;
swizzleSuccess &= [[self class] copyMethod:@selector(_specialValidateMenuItem:) 
                                 fromClass:[self class] 
                                   toClass:MessageViewer];
swizzleSuccess &= [[self class] copyMethod:@selector(unsubscribeSelectedMessages:) 
                                 fromClass:[self class] 
                                   toClass:MessageViewer];

但是,当我尝试做同样的事情时,它不起作用:

//  //Copy the method to the original MessageViewer
swizzleSuccess &= [[self class] copyMethod:@selector(_specialInitMessageViewer:)
                                 fromClass:[self class]
                                   toClass:MessageViewer];  

以下是调酒方法:

+ (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)altSel inClass:(Class)cls

// For class (cls), swizzle the original selector with the new selector.
    //debug lines to try to figure out why swizzling is failing.
//  if (!cls  ||  !origSel) 
    //          NSLog(@"Something was null.  Uh oh.");
    //
Method origMethod = class_getInstanceMethod(cls, origSel);

if (!origMethod) 
    NSLog(@"Swizzler -- original method %@ not found for class %@", NSStringFromSelector(origSel), 
          [cls className]);
    return NO;

    //if (!altSel) NSLog(@"altSel null.  :(");
Method altMethod = class_getInstanceMethod(cls, altSel);
if (!altMethod) 
    NSLog(@"Swizzler -- alternate method %@ not found for class %@", NSStringFromSelector(altSel), 
          [cls className]);
    return NO;


method_exchangeImplementations(origMethod, altMethod);

return YES;




+ (BOOL) copyMethod:(SEL)sel fromClass:(Class)fromCls toClass:(Class)toCls

    // copy a method from one class to another.
    Method method = class_getInstanceMethod(fromCls, sel);
    if (!method)
    
        NSLog(@"copyMethod-- method %@ could not be found in class %@", NSStringFromSelector(sel),
              [fromCls className]);
        return NO;
    
    class_addMethod(toCls, sel, 
                    class_getMethodImplementation(fromCls, sel), 
                    method_getTypeEncoding(method));
    return YES;

调用 class_getInstanceMethod 似乎失败了,因为我在日志中收到错误消息。这发生在我自己的类中的方法以及 MessageViewer 的初始化方法中。

这里有一些我没有考虑到的问题吗?

【问题讨论】:

您的代码格式不正确,看起来有点乱。您需要每个代码行至少有 4 个空格,以免被破坏。 【参考方案1】:

如果你想混搭类方法,那你为什么要使用class_getInstanceMethod() 函数而不是class_getClassMethod()

【讨论】:

【参考方案2】:

您应该只使用JRSwizzle,而不是尝试手动实现swizzling。需要注意的是,JRSwizzle 的+jr_swizzleClassMethod:withClassMethod:error: 实现只是一个存根,但您可以简单地在类的元类上调用+jr_swizzleMethod:withMethod:error(例如,只需传递klass->isa 而不是klass(其中klass 是您的类) '在调酒))。

【讨论】:

谢谢,Kevin,JRSwizzle 包看起来棒极了!现在,我是否仍然负责将我的类方法复制到我想要调配的类中,或者有没有办法可以在 jr_swizzleClassMethod 调用中指出这一点? (如果很明显,我很抱歉;我对 ObjC 很陌生) jr_swizzleMethod:withMethod:error: 为您处理一切。它将您的方法复制到目标类中,并将 IMP 与您尝试覆盖的方法交换。 好的,假设我在类 Foo 中有一个方法 newMethod,我想用类 Bar 中的方法 origMethod 来调整它。如何标注我在哪些类中切换了哪些方法? 对不起,这有点误导。仅当您覆盖超类中的方法时才会发生复制。通常在混合方法时,您将新方法写入原始类的类别中。试图在类之间复制方法没有任何意义,因为编译器会在编译原始方法时考虑到错误的类。你应该把你的 newMethod 写在一个类 Bar 上,然后将它混合。 好吧,这更有意义。我知道将一个方法完全复制到另一个类很奇怪,但我正在为 Mail.app 编写一个插件,所以我缺少选项,因为它没有适当的插件架构。我会试试这个。再次感谢!

以上是关于无法调配类方法的主要内容,如果未能解决你的问题,请参考以下文章

混合单个实例,而不是类

Objective-C中方法调配的正确方法

Swift 中的方法调配

Project: 如何调配项目中的资源?

使用方法调配一次在所有 UIViewController 实例上更改 iOS13 上的 modalPresentationStyle

为什么无法在Main方法中调用外部类的clone方法?