【问题标题】: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 个空格,以免被破坏。

标签: objective-c macos nsbundle


【解决方案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 编写一个插件,所以我缺少选项,因为它没有适当的插件架构。我会试试这个。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2023-03-11
    • 2019-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多