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。最常見的用法是用於調試沒有源碼的類實現等等。