做iOS的朋友都知道或聽說runtime,這個東西很像java的反射機制,但功能遠勝於java的反射。通過runtime我們可以動態的向一個類中添加屬性、成員變量、方法,以及對其進行讀寫訪問。
一、runtime簡介
RunTime簡稱運行時。OC就是運行時機制,也就是在運行時候 的一些機制,其中最主要的是消息機制。
對於C語言,函數的調用在編譯的時候會決定調用哪個函數。
對於OC的函數,屬於動態調用過程,在編譯的時候並不能決定真正調用哪個函數,只有在真正運行的時候才會根據函數的名稱
找到對應的函數來調用。
事實證明:
在編譯階段,OC可以調用任何函數,即使這個函數並未實現,只要聲明過就不會報錯。
在編譯階段,C語言調用未實現的函數就會報錯。
二、runtime作用
1.發送消息
方法調用的本質,就是讓對象發送消息。
objc_msgSend,
只有對象才能發送消息,因此以objc開頭.
使用消息機制前提,必須導入#import <objc/message.h>
消息機制簡單使用
消息機制原理:對象根據方法編號SEL去映射表查找對應的方法實現
// 創建person對象 Person *p = [[Person alloc] init]; // 調用對象方法 [p eat]; // SEL:方法編號,根據方法編號就可以找到對應方法實現 [p performSelector:@selector(eat)]; // 本質:讓對象發送消息 objc_msgSend(p, @selector(eat)); // 調用類方法的方式:兩種 // 第一種通過類名調用本質類名轉換成類對象 [Person eat]; // 第二種通過類對象調用 [[Person class] eat]; [personClass performSelector:@selector(eat)]; // 用類名調用類方法,底層會自動把類名轉換成類對象調用 // 本質:讓類對象發送消息 objc_msgSend([Person class], @selector(eat));
2.交換方法
開發使用場景:系統自帶的方法功能不夠,給系統自帶的方法擴展一些功能,並且保持原有的功能。
方式一:繼承系統的類,重寫方法.
方式二:使用runtime,交換方法.
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 需求:給imageNamed方法提供功能,每次加載圖片就判斷下圖片是否加載成功。 // 步驟一:先搞個分類,定義一個能加載圖片並且能打印的方法+ (UIImage *)xmg_imageNamed:(NSString *)imageName; // 步驟二:交換imageNamed和xmg_imageNamed的實現,就能調用xmg_imageNamed,間接調用xmg_imageNamed的實現。 UIImage *image = [UIImage imageNamed:@"123"]; imageNamed: 實現方法:底層調用PH_imageNamed 本質:交換兩個方法的實現imageNamed和PH_imageNamed方法 調用imageNamed其實就是調用PH_imageNamed imageNamed加載圖片,並不知道圖片是否加載成功 以後調用imageNamed的時候,就知道圖片是否加載 } @end @implementation UIImage (Image) // 加載分類到內存的時候調用 + (void)load { // 交換方法實現,方法都是定義在類裡面 // class_getMethodImplementation:獲取方法實現 // class_getInstanceMethod:獲取對象 // class_getClassMethod:獲取類方法 // IMP:方法實現 // imageNamed // Class:獲取哪個類方法 // SEL:獲取方法編號,根據SEL就能去對應的類找方法 Method imageNameMethod = class_getClassMethod([UIImage class], @selector(imageNamed:)); Method PH_imageNameMethod = class_getClassMethod([UIImage class], @selector(PH_imageNamed:)); // 交換方法實現 method_exchangeImplementations(imageNameMethod, PH_imageNameMethod); } } // 不能在分類中重寫系統方法imageNamed,因為會把系統的功能給覆蓋掉,而且分類中不能調用super. // 既能加載圖片又能打印 + (UIImage *)PH_imageNamed:(NSString *)imageName { // 加載圖片 UIImage *image = [UIImage PH_imageNamed:imageName]; // 2.判斷功能 if (image == nil) { NSLog(@"加載為空"); } return image; } @end
3.動態添加方法
開發使用場景:如果一個類方法非常多,加載類到內存的時候也比較耗費資源,需要給每個方法生成映射表,可以使用動態給某個類,添加方法解決。
簡單使用
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. Person *p = [[Person alloc] init]; // 默認person,沒有實現eat方法,可以通過performSelector調用,但是會報錯。 // 動態添加方法就不會報錯 [p performSelector:@selector(eat)]; } @end @implementation Person // void(*)() // 默認方法都有兩個隱式參數, 默認一個方法都有兩個參數,self,_cmd,隱式參數 self:方法調用者 _cmd:調用方法的編號 void eat(id self,SEL sel) { NSLog(@"%@ %@",self,NSStringFromSelector(sel)); } // 當一個對象調用未實現的方法,會調用這個方法處理,並且會把對應的方法列表傳過來. // 剛好可以用來判斷,未實現的方法是不是我們想要動態添加的方法 <!--動態添加方法,首先實現這個resolveInstanceMethod--> <!-- resolveInstanceMethod調用:當調用了沒有實現的方法沒有實現就會調用resolveInstanceMethod--> <!-- resolveInstanceMethod作用:就知道哪些方法沒有實現,從而動態添加方法--> <!-- sel:沒有實現方法--> + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(eat)) { // 動態添加eat方法 // 第一個參數:給哪個類添加方法 // 第二個參數:添加方法的方法編號 // 第三個參數:添加方法的函數實現(函數地址) // 第四個參數:函數的類型,(返回值+參數類型) v:void @:對象->self :表示SEL->_cmd class_addMethod(self, @selector(eat), eat, "v@:"); } return [super resolveInstanceMethod:sel]; } @end
4.給分類添加屬性
原理:給一個類聲明屬性,其實本質就是給這個類添加關聯,並不是直接把這個值的內存空間添加到類存空間。
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 給系統NSObject類動態添加屬性name NSObject *objc = [[NSObject alloc] init]; objc.name = @"abc"; NSLog(@"%@",objc.name); } @end // 定義關聯的key static const char *key = "name"; - (void)setName:(NSString *)name { // 添加屬性,跟對象 // 給某個對象產生關聯,添加屬性 // object:給哪個對象添加屬性 // key:屬性名,根據key去獲取關聯的對象 ,void * == id // value:關聯的值 // policy:策略 objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSString *)name { return objc_getAssociatedObject(self, @"name"); }
以上就是iOS中runtime的使用總結,本篇文章主要是原理和用法總結,runtime的功能很強大,還需要朋友們多多學習和研究才可以。希望本文對大家有所幫助。