佳为好友

转:MethodSwizzling

创建:2011.06.07

[自:这个版本介绍了原理,但是,真实使用的时候,我们要使用文中提到的JRSwizzle库。因 为,它的兼容性和通用性更好。]


转:http://www.cocoadev.com/index.pl?MethodSwizzling


ExtendingClasses - Lightweight modification of a single method at runtime

The 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 (AppKitFoundationKit, 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


+++++

posted on 2012-12-30 10:53 佳为好友 阅读(199) 评论(0)  编辑 收藏 引用 所属分类: 非UI


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2012年12月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

留言簿(1)

随笔分类

搜索

最新评论

评论排行榜