我們知道,OC是一門動態運行時語言。我們也知道,OC中的函數調用本質上其實是消息的發送:objc_MsgSend(someObject, @selector(methodName), parameters)
。所有要調用的函數,編譯階段是無法確定的,只有到運行的時候才能確定!正因為有此特性,所以在運行階段,我們都還可以做很多事,而要把本來應該實現的方法A,偷偷換成方法B,便是其中的一種,這種技術叫做:Method Swizzling(方法交換)!
具體相關的概念就不再啰嗦了,網上都有很多了,可以自行google或者查閱《Effective Objective-C 2.0 編寫高質量iOS與OS X 代碼的52個有效方法》的第13條,介紹的都比較詳細了。這里就總結一下相關的應用!
調試用
假如每次調用NSString的lowerCaseString方法時,不要每次都去NSLog,希望直接調用系統的方法時每次都可以輸出log,那么這時候就可以用到該技術:
@interface NSString (XPPMyAdditions)
- (NSString *)xpp_myLowerCaseString;
@end
@implementation NSString (XPPMyAdditions)
- (NSString *)xpp_myLowerCaseString
{
NSString *LowerString = [self xpp_myLowerCaseString];
NSLog(@"%@->%@", self, LowerString);
return LowerString;
}
接下來做的就是把這個分類里面的方法和原生的方法交換一下:
void swizzling(){
Method originalMtd = class_getInstanceMethod([UIF class], @selector(xpp_myLowerCaseString));
Method swappedMtd = class_getInstanceMethod([NSString class], @selector(lowercaseString));
method_exchangeImplementations(originalMtd, swappedMtd);
}
那么在以后,直接調用系統原生的lowercaseString方法時,實際上調用的是分類里面的方法,在達到原來目的的同時,還能輸出結果,非常方便!
注意:交換過后調用NSString原生的lowerCaseString實際上是調用xpp_myLowerCaseString;而在現在的xpp_myLowerCaseString里面,其實[self xpp_myLowerCaseString]實際上是調用了系統的lowerCaseString方法達到把字符串變為小寫的目的。所以,不會陷入死循環!
一次性更改
假如今天你接到產品的需求,把App里面所有的字體減小兩個點。你怎么辦?搜索找工程里面的systemFontOfSize:
方法,挨個把字號-2;好,沒問題,可以。那第二天她覺得這個字太小了,再加1個點吧^?_?^。
這時候,方法交換技術派上用成了:
@interface UIFont (XppMyAdditions)
+ (UIFont *)xpp_systemFontOfSize:(CGFloat)size;
@end
@implementation UIFont (XppMyAdditions)
+ (UIFont *)xpp_systemFontOfSize:(CGFloat)size
{
UIFont *font = [self xpp_systemFontOfSize:(size-2)];
NSLog(@"%f---->%f",size, font.pointSize);
return font;
}
接下來,再把系統的方法和該方法交換一下:
Method m1 = class_getClassMethod([UIFont class], @selector(systemFontOfSize:));
Method m2 = class_getClassMethod([UIFont class], @selector(xpp_systemFontOfSize:));
method_exchangeImplementations(m1, m2);
注意:這里是類方法!不是實例方法,所以注意API的調用和分類方法要匹配,否則不會報錯,但是你會發現沒效果