標籤:ios 動態語言 運行時 method swizzling
C語言是靜態語言,它的工作方式是通過函數調用,這樣在編譯時間我們就已經確定程式如何啟動並執行。而Objective-C是動態語言,它並非通過調用類的方法來執行功能,而是給對象發送訊息,對象在接收到訊息之後會去找匹配的方法來運行。這種做法就把C語言在編譯時間的工作挪到了運行時來做,可以獲得額外的靈活性。
在Objective-C中有個@selector,在很多地方被翻譯成“選擇子”。實際上,對於類的執行個體對象來說,類的方法是用一個數字來代表的,並非是我們看到的一個長長的帶著:這個字元的一串字串。通過這個@selector就可以把這個方法的名字轉成所對應的數字。當一個類確定後,實際上每個方法的@selector的值就是固定的,說到這裡,你一定可以想到method swizzling是什麼一個東東了,沒錯,如果我們原來有個A方法,@selector(A)就是一個數字,我們的對象在接收到一個訊息後就去尋找對應的方法並運行——如果,我們把@selector(B)的數字換成了原來@selector(A)的數字,那麼此時對象雖然受到A訊息,但會去運行B方法!
在iOS中,這是完全可以實現的,那麼我們什麼時候會需要這麼做呢?我覺得有2個時候:
1. 破解,毋庸諱言,這絕對是破解的利器,不解釋了。
2. 在開發調試過程中,如果你對某個庫裡的方法不確定或者覺得需要擴充的時候,你可以自己寫一個去代替它。因為Objective-C是有Category的,所以擴充功能沒啥必要,但調試時增加一些列印語句是很方便實際的。
舉個例子,NSString裡面的lowercaseString方法,如果我不太清楚這個方法都幹了什麼,我就可以自己寫個方法來替換它,這個方法裡面增加列印語句,這樣log裡面就一目瞭然了。
首先需要增加一個NSString的Category
@interface NSString (wzTest)- (NSString*)myLowerString;@end@implementation NSString (wzTest)- (NSString*)myLowerString{ NSString *lowerString = [self myLowerString]; NSLog(@"%@ => %@", self, lowerString); return lowerString;}@end 這裡有一個地方解釋一下,在myLowerString方法裡面,看起來遞迴調用了自身。但是,我們會用原來的lowercaseString方法去替換自己寫的myLowerString方法,所以這裡並沒有調用自身,而是調用了原來的lowercaseString方法。這點請注意一下。
其次替換系統原來的lowercaseString方法,使用runtime裡面的方法。
Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString)); Method swapMethod = class_getInstanceMethod([NSString class], @selector(myLowerString)); method_exchangeImplementations(originalMethod, swapMethod); NSString *testStr = @"thIs is THE Test STRING"; NSLog(@"lowerString of testStr=%@", [testStr lowercaseString]);
我們來看一下log的結果:
2014-05-29 22:17:55.514 testTableView[1582:a0b] thIs is THE Test STRING => this is the test string
2014-05-29 22:17:55.514 testTableView[1582:a0b] lowerString of testStr=this is the test string
我們可以看到,系統中使用是繼續使用lowercaseString方法的,不過實際執行的是我們新增的方法。當你不需要這樣做的時候,關閉method swizzling方法就可以恢複了。
我們的例子中是增加了列印語句,實際上還可以做更多地操作。這在用第三方庫調試的時候是非常有用的一個方法,可以很方便的查看變數的內容或做一些其他工作。調試結束後,關閉method swizzling就可以正常的工作。