Can't swizzle class methods
I'm working on making a plugin for Mail.app. I'd like the plugin to add a button to Mail's toolbar. To do this, I decided the best approach would be to call the function to add this button in the MessageViewer's initialize method (MessageViewer is the class for Mail.app's FirstResponder). The code I'm modifying seems to work nicely:
+ (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];
However, when I try to do the same, it doesn't work:
// //Copy the method to the original MessageViewer
swizzleSuccess &= [[self class] copyMethod:@selector(_specialInitMessageViewer:)
fromClass:[self class]
toClass:MessageViewer];
Here are the swizzling methods:
+ (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(@"Swi开发者_StackOverflow中文版zzler -- 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;
}
It seems to be failing in the call to class_getInstanceMethod, because I get an error in the log. This happens for both the method inside of my own class, as well as for the MessageViewer's initialize method.
Are there some gotchas I'm not taking into account here?
If you want to swizzle class methods, then why are you using the class_getInstanceMethod()
function instead of class_getClassMethod()
?
Instead of trying to implement swizzling manually, you should just use JRSwizzle. One word of caution, JRSwizzle's implementation of +jr_swizzleClassMethod:withClassMethod:error:
is just a stub, but you can simply call +jr_swizzleMethod:withMethod:error
on the class's metaclass instead (e.g. just pass klass->isa
instead of klass
(where klass
is the class you're swizzling)).
精彩评论