objective-c的instance method调用实际上是查表再通过C形式调用的过程,这个函数表是可以操作的,这样就给了我们可以在运行时修改的机会,这种做法叫swizzle .
<objc/runtime.h>里面提供了API,我们以调换NSString的lowercaseString与我们的category里面的stoneLowercaseString为目标。实现swizzle基本上会用到category,先看category里面的代码:
.h文件
===================================
#import
<Foundation/Foundation.h>
@interface NSString (Stone)
- ( NSString *)stoneLowercaseString;
@interface NSString (Stone)
- ( NSString *)stoneLowercaseString;
@end
.m文件
===================================
#import
"NSString+Stone.h"
@implementation NSString (Stone)
- ( NSString *)stoneLowercaseString{
// 递归调用?不是,实际上在运行到这里之前我们已经做了 swap ,所以这里
// 调用的是原版的 lowercaseString ,达到了调用原方法的目的。
NSString *lowercase = [ self stoneLowercaseString ];
NSLog ( @"[Stone] %@" , lowercase);
return lowercase;
}
@implementation NSString (Stone)
- ( NSString *)stoneLowercaseString{
// 递归调用?不是,实际上在运行到这里之前我们已经做了 swap ,所以这里
// 调用的是原版的 lowercaseString ,达到了调用原方法的目的。
NSString *lowercase = [ self stoneLowercaseString ];
NSLog ( @"[Stone] %@" , lowercase);
return lowercase;
}
@end
方法的swap以及测试,swizzle的调用时机越早越好,一般会写在app的didLaunch事件中。
===================================
// swizzle stuff
// 这里注意不要写成了 class_getClassMethod
Method originalMethod = class_getInstanceMethod ([ NSString class ], @selector (lowercaseString));
Method stoneMethod = class_getInstanceMethod ([ NSString class ], @selector (stoneLowercaseString));
method_exchangeImplementations (originalMethod, stoneMethod);
// test
NSString *test = @"Gundam Exia" ;
// 这里注意不要写成了 class_getClassMethod
Method originalMethod = class_getInstanceMethod ([ NSString class ], @selector (lowercaseString));
Method stoneMethod = class_getInstanceMethod ([ NSString class ], @selector (stoneLowercaseString));
method_exchangeImplementations (originalMethod, stoneMethod);
// test
NSString *test = @"Gundam Exia" ;
NSLog(@"lower = %@", [test lowercaseString]);
输出
===================================
2015-09-08 09:59:08.621 SwizzleDemo[16503:811706] [Stone] gundam exia
2015-09-08 09:59:08.622 SwizzleDemo[16503:811706] lower = gundam exia
*注意点
不要为了用swizzle而用swizzle,此技巧一旦出现问题的话会非常难以调试和debug。因此,除非是常规办法没法实现或者实现起来很麻烦的需求,一般避免用swizzle。最常见的用法是用于调试没有源码的类实现等等。