動態的交換方法能夠給項目中大量已經使用的方法 進行攔截增加操作
實踐:利用運行時交換系統的ImageNamed:方法
應用背景
當系統需要適配ios7和ios8時可能會有顯示不同圖片的需求,但在老項目上開發ios7程序時並未考慮到ios8的適配,當項目上有幾百處地方用到ImageNamed:方法時,如果選用最直接的辦法,在該方法之前進行判斷,如果為ios8就顯示另外一張圖片,這樣的工作量明顯會很大,所以可以用運行時的方法來解決。
新建一個項目,准備兩張圖片,一張圖片名為close為ios7而准備,一張close_os8為ios8而准備
在storyboard里添加兩個個UIImageView控件
@interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *img1; @property (weak, nonatomic) IBOutlet UIImageView *img2; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.img1.image = [UIImage imageNamed:@"close"]; self.img2.image = [UIImage imageNamed:@"fire"]; } @end
接下來使用運行時的方法交換imageNamed:的實現,先為UiImage添加一個自定義的創建圖片的分類,此時需要導入一個運行時框架 #import<objc/runtime.h>,
代碼如下:
/** * 自定義創建圖片方法 */ + (instancetype)imageWithName:(NSString *)name { NSString *imgN = name; double version = [[UIDevice currentDevice].systemVersion doubleValue]; NSLog(@"%f",version); if (version >= 8.0) { imgN = [NSString stringWithFormat:@"%@_os8",name]; NSLog(@"%@",imgN); } // 此時imageWithName:的實現在運行時已經被改為imageNamed:的實現 return [self imageWithName:imgN]; }
在load方法里面實現方法的交換,該方法只會被加載一次
/** * 類第一次加載進內存的時候會調用 */ + (void)load { // 運行時交換兩個方法的實現 Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:)); Method m2 = class_getClassMethod([UIImage class], @selector(imageWithName:)); method_exchangeImplementations(m1, m2); }
如此就利用運行時機制實現了系統方法和自定義方法互相交換實現
需要了解的知識點
一、什么是運行時
運行時是一套純C語言的API,編譯器最終都會將OC代碼轉化為運行時代碼。
二、運行時常用函數
1、<objc/runtime.h>
* 獲得某個類的類方法 Method class_getClassMethod(Class cls, SEL name)
* 獲得某個對象的對象方法 Method class_getInstanceMethod(Class cls, SEL name)
* 交換兩個方法的實現 void method_exchangeImplementations(Method m1, Method m2)
* 關聯對象(將值value與對象object關聯起來) void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
參數key:將來可以通過key取出這個存儲的值
參數policy:存儲策略(assign、copy、retain)
* 利用參數key將對象中存儲的對應值取出 id objc_getAssociatedObject(id object, const void *key)
* 獲得某個類的所有成員變量 Ivar *class_copyIvarList(Class cls, unsigned int *outCount)(outCount會返回成員變量的總數)
* 獲得成員變量的名字 const char *ivar_getName(Ivar v)
* 獲得成員變量的類型 const char *ivar_getTypeEncoding(Ivar v)
* 釋放內存 void free(void *) (當C語言函數名中包含了copy、create、retain、new等詞語,那么就需要在最后釋放資源)
2、<objc/message.h>
* 給某個對象發送某個消息 void objc_msgSend(void)