Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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(@"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;
}

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?

like image 903
Aaron Avatar asked Dec 09 '22 12:12

Aaron


1 Answers

If you want to swizzle class methods, then why are you using the class_getInstanceMethod() function instead of class_getClassMethod()?

like image 78
NSResponder Avatar answered Dec 26 '22 17:12

NSResponder