我们要使用文中提到的JRSwizzle库。因 为,它的兼容性和通用性更好。]
转:http://www.cocoadev.com/index.pl?MethodSwizzling
ExtendingClasses - Lightweight modification of a single method at runtimeThe Objective-C runtime lets you modify the mappings from a selector (method name) to an implementation (the method code itself). This allows you to "patch" methods in code you don't have the source to (AppKit, FoundationKit, etc). Unlike creating a category method with the same name as the original method (effectively replacing the original method), MethodSwizzling lets your replacement method make use of the original method, almost like subclassing.
This is best used in cases where a single method needs substitution or extension; If you need to modify many behaviors of a class, you may be better off usingClassPosing.
December 29, 2007: [http://mjtsai.com/blog/2007/12/29/jrswizzle/] Jonathan Rentzsch has started a project, JRSwizzle? [http://github.com/rentzsch/jrswizzle], to implement method swizzling correctly with different versions of the Objective-C runtime:
There’s at least four swizzling implementations floating around. Here’s a comparison chart to help you make sense of how they relate to each other and why JRSwizzle?exists.
An illustration
For instance, let's pretend there's a class called Foo that implements the following method:
// returns the login name of the current user - (NSString *)fooBar;
Now we've got applications that call [[Foo sharedFoo] fooBar] all over the place; We'd like to modify the functionality of the fooBar method to append something silly to the result of the original fooBar return value.
So, we'll implement a category method that does the work:
@implementation Foo(specialfooBar) // returns the login name of the current user plus some other junk - (NSString *)myfooBar { return [[self myfooBar] stringByAppendingString:@", bigtime luser"]; } @end
"But wait", I hear you saying, "the myfooBar method is calling itself, generating infinite recursion!" Well, that would be the case except for the fact that we're going to do some MethodSwizzling and swap the implementations of the two methods! Here's the deal: Right now, any code that calls -[Foo fooBar] is going to use the original implementation (let's call that "A"), and any code that calls -[Foo myfooBar] is going to use our new implementation (let's call that "B"). To summarize:
selector "fooBar" -> implementation "A" selector "myfooBar" -> implementation "B" - implementation "A" returns @"username" - implementation "B" calls "myfooBar", appends some silly text, and returns that
So right now, calling "fooBar" invokes implementation "A" like it always has; Calling "myfooBar", however, invokes implementation "B", which in turn calls "myfooBar", which invokes implementation "B", etc, leading to the unwanted infinite recursion.
// Do the swizzling (see below for the function code) MethodSwizzle([Foo class], @selector(fooBar) @selector(myfooBar));
After swizzling the two methods, we have the following situation:
selector "fooBar" -> implementation "B" selector "myfooBar" -> implementation "A" - implementation "A" returns @"username" - implementation "B" calls "myfooBar", appends some silly text, and returns that
Now, after the swizzling, anybody who calls "fooBar" will invoke implementation "B", which calls "myfooBar" (invoking implementation "A" and appending some silly text). In short, calling "fooBar" will now return @"username, bigtime luser"!
Hopefully this makes sense to people!
--JackNutting
The MethodSwizzle function
Here's a C-function that does the trick:
#import </usr/include/objc/objc-class.h> void MethodSwizzle(Class aClass, SEL orig_sel, SEL alt_sel) { Method orig_method = nil, alt_method = nil; // First, look for the methods orig_method = class_getInstanceMethod(aClass, orig_sel); alt_method = class_getInstanceMethod(aClass, alt_sel); // If both are found, swizzle them if ((orig_method != nil) && (alt_method != nil)) { char *temp1; IMP temp2; temp1 = orig_method->method_types; orig_method->method_types = alt_method->method_types; alt_method->method_types = temp1; temp2 = orig_method->method_imp; orig_method->method_imp = alt_method->method_imp; alt_method->method_imp = temp2; } }
Here's code that uses it:
@interface T : NSObject { } - (void)swizzleMethod; @end @interface T (AltMethod) - (void)altMethod; @end int main (int argc, const char * argv[]) { T *t; t = [[T alloc] init]; [t swizzleMethod]; NSLog(@"Methods swizzled"); MethodSwizzle([T class], @selector(swizzleMethod), @selector(altMethod)); [t swizzleMethod]; [t release]; return 0; } @implementation T - (void)swizzleMethod { NSLog(@"Original method called"); } @end @implementation T (AltMethod) - (void)altMethod { NSLog(@"Alternative method called"); [self altMethod]; } @end
Note the use of a category, just to check everything works correctly.Here's the output as logged:
2002-04-09 17:23:58.144 Swizzling[2285] Original method called 2002-04-09 17:23:58.148 Swizzling[2285] Methods swizzled 2002-04-09 17:23:58.150 Swizzling[2285] Alternative method called 2002-04-09 17:23:58.152 Swizzling[2285] Original method called
+++++