无法调配类方法
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 编写一个插件,所以我缺少选项,因为它没有适当的插件架构。我会试试这个。再次感谢!以上是关于无法调配类方法的主要内容,如果未能解决你的问题,请参考以下文章
使用方法调配一次在所有 UIViewController 实例上更改 iOS13 上的 modalPresentationStyle