總結(jié)iOS中runtime的使用
做iOS的朋友都知道或聽說runtime,這個(gè)東西很像java的反射機(jī)制,但功能遠(yuǎn)勝于java的反射。通過runtime我們可以動態(tài)的向一個(gè)類中添加屬性、成員變量、方法,以及對其進(jìn)行讀寫訪問。
一、runtime簡介
RunTime簡稱運(yùn)行時(shí)。OC就是運(yùn)行時(shí)機(jī)制,也就是在運(yùn)行時(shí)候 的一些機(jī)制,其中最主要的是消息機(jī)制。
對于C語言,函數(shù)的調(diào)用在編譯的時(shí)候會決定調(diào)用哪個(gè)函數(shù)。
對于OC的函數(shù),屬于動態(tài)調(diào)用過程,在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù),只有在真正運(yùn)行的時(shí)候才會根據(jù)函數(shù)的名稱
找到對應(yīng)的函數(shù)來調(diào)用。
事實(shí)證明:
在編譯階段,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要聲明過就不會報(bào)錯(cuò)。
在編譯階段,C語言調(diào)用未實(shí)現(xiàn)的函數(shù)就會報(bào)錯(cuò)。
二、runtime作用
1.發(fā)送消息
方法調(diào)用的本質(zhì),就是讓對象發(fā)送消息。
objc_msgSend,
只有對象才能發(fā)送消息,因此以objc開頭.
使用消息機(jī)制前提,必須導(dǎo)入#import <objc/message.h>
消息機(jī)制簡單使用
消息機(jī)制原理:對象根據(jù)方法編號SEL去映射表查找對應(yīng)的方法實(shí)現(xiàn)
// 創(chuàng)建person對象 Person *p = [[Person alloc] init]; // 調(diào)用對象方法 [p eat]; // SEL:方法編號,根據(jù)方法編號就可以找到對應(yīng)方法實(shí)現(xiàn) [p performSelector:@selector(eat)]; // 本質(zhì):讓對象發(fā)送消息 objc_msgSend(p, @selector(eat)); // 調(diào)用類方法的方式:兩種 // 第一種通過類名調(diào)用本質(zhì)類名轉(zhuǎn)換成類對象 [Person eat]; // 第二種通過類對象調(diào)用 [[Person class] eat]; [personClass performSelector:@selector(eat)]; // 用類名調(diào)用類方法,底層會自動把類名轉(zhuǎn)換成類對象調(diào)用 // 本質(zhì):讓類對象發(fā)送消息 objc_msgSend([Person class], @selector(eat));
2.交換方法
開發(fā)使用場景:系統(tǒng)自帶的方法功能不夠,給系統(tǒng)自帶的方法擴(kuò)展一些功能,并且保持原有的功能。
方式一:繼承系統(tǒng)的類,重寫方法.
方式二:使用runtime,交換方法.
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 需求:給imageNamed方法提供功能,每次加載圖片就判斷下圖片是否加載成功。 // 步驟一:先搞個(gè)分類,定義一個(gè)能加載圖片并且能打印的方法+ (UIImage *)xmg_imageNamed:(NSString *)imageName; // 步驟二:交換imageNamed和xmg_imageNamed的實(shí)現(xiàn),就能調(diào)用xmg_imageNamed,間接調(diào)用xmg_imageNamed的實(shí)現(xiàn)。 UIImage *image = [UIImage imageNamed:@"123"]; imageNamed: 實(shí)現(xiàn)方法:底層調(diào)用PH_imageNamed 本質(zhì):交換兩個(gè)方法的實(shí)現(xiàn)imageNamed和PH_imageNamed方法 調(diào)用imageNamed其實(shí)就是調(diào)用PH_imageNamed imageNamed加載圖片,并不知道圖片是否加載成功 以后調(diào)用imageNamed的時(shí)候,就知道圖片是否加載 } @end @implementation UIImage (Image) // 加載分類到內(nèi)存的時(shí)候調(diào)用 + (void)load { // 交換方法實(shí)現(xiàn),方法都是定義在類里面 // class_getMethodImplementation:獲取方法實(shí)現(xiàn) // class_getInstanceMethod:獲取對象 // class_getClassMethod:獲取類方法 // IMP:方法實(shí)現(xiàn) // imageNamed // Class:獲取哪個(gè)類方法 // SEL:獲取方法編號,根據(jù)SEL就能去對應(yīng)的類找方法 Method imageNameMethod = class_getClassMethod([UIImage class], @selector(imageNamed:)); Method PH_imageNameMethod = class_getClassMethod([UIImage class], @selector(PH_imageNamed:)); // 交換方法實(shí)現(xiàn) method_exchangeImplementations(imageNameMethod, PH_imageNameMethod); } } // 不能在分類中重寫系統(tǒng)方法imageNamed,因?yàn)闀严到y(tǒng)的功能給覆蓋掉,而且分類中不能調(diào)用super. // 既能加載圖片又能打印 + (UIImage *)PH_imageNamed:(NSString *)imageName { // 加載圖片 UIImage *image = [UIImage PH_imageNamed:imageName]; // 2.判斷功能 if (image == nil) { NSLog(@"加載為空"); } return image; } @end
3.動態(tài)添加方法
開發(fā)使用場景:如果一個(gè)類方法非常多,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源,需要給每個(gè)方法生成映射表,可以使用動態(tài)給某個(gè)類,添加方法解決。
簡單使用
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. Person *p = [[Person alloc] init]; // 默認(rèn)person,沒有實(shí)現(xiàn)eat方法,可以通過performSelector調(diào)用,但是會報(bào)錯(cuò)。 // 動態(tài)添加方法就不會報(bào)錯(cuò) [p performSelector:@selector(eat)]; } @end @implementation Person // void(*)() // 默認(rèn)方法都有兩個(gè)隱式參數(shù), 默認(rèn)一個(gè)方法都有兩個(gè)參數(shù),self,_cmd,隱式參數(shù) self:方法調(diào)用者 _cmd:調(diào)用方法的編號 void eat(id self,SEL sel) { NSLog(@"%@ %@",self,NSStringFromSelector(sel)); } // 當(dāng)一個(gè)對象調(diào)用未實(shí)現(xiàn)的方法,會調(diào)用這個(gè)方法處理,并且會把對應(yīng)的方法列表傳過來. // 剛好可以用來判斷,未實(shí)現(xiàn)的方法是不是我們想要動態(tài)添加的方法 <!--動態(tài)添加方法,首先實(shí)現(xiàn)這個(gè)resolveInstanceMethod--> <!-- resolveInstanceMethod調(diào)用:當(dāng)調(diào)用了沒有實(shí)現(xiàn)的方法沒有實(shí)現(xiàn)就會調(diào)用resolveInstanceMethod--> <!-- resolveInstanceMethod作用:就知道哪些方法沒有實(shí)現(xiàn),從而動態(tài)添加方法--> <!-- sel:沒有實(shí)現(xiàn)方法--> + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(eat)) { // 動態(tài)添加eat方法 // 第一個(gè)參數(shù):給哪個(gè)類添加方法 // 第二個(gè)參數(shù):添加方法的方法編號 // 第三個(gè)參數(shù):添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址) // 第四個(gè)參數(shù):函數(shù)的類型,(返回值+參數(shù)類型) v:void @:對象->self :表示SEL->_cmd class_addMethod(self, @selector(eat), eat, "v@:"); } return [super resolveInstanceMethod:sel]; } @end
4.給分類添加屬性
原理:給一個(gè)類聲明屬性,其實(shí)本質(zhì)就是給這個(gè)類添加關(guān)聯(lián),并不是直接把這個(gè)值的內(nèi)存空間添加到類存空間。
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 給系統(tǒng)NSObject類動態(tài)添加屬性name NSObject *objc = [[NSObject alloc] init]; objc.name = @"abc"; NSLog(@"%@",objc.name); } @end // 定義關(guān)聯(lián)的key static const char *key = "name"; - (void)setName:(NSString *)name { // 添加屬性,跟對象 // 給某個(gè)對象產(chǎn)生關(guān)聯(lián),添加屬性 // object:給哪個(gè)對象添加屬性 // key:屬性名,根據(jù)key去獲取關(guān)聯(lián)的對象 ,void * == id // value:關(guān)聯(lián)的值 // policy:策略 objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSString *)name { return objc_getAssociatedObject(self, @"name"); }
以上就是iOS中runtime的使用總結(jié),本篇文章主要是原理和用法總結(jié),runtime的功能很強(qiáng)大,還需要朋友們多多學(xué)習(xí)和研究才可以。希望本文對大家有所幫助。
相關(guān)文章
iOS開發(fā)中UIPopoverController的使用詳解
這篇文章主要介紹了iOS開發(fā)中UIPopoverController的使用,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-11-11詳解iOS多線程之2.NSThread的加鎖@synchronized
這篇文章主要介紹了詳解iOS多線程之2.NSThread的加鎖@synchronized,有需要的小伙伴可以參考下。2016-11-11全面解析iOS中同步請求、異步請求、GET請求、POST請求
通過本文給大家全面解析了iOS中同步請求、異步請求、GET請求、POST請求,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-08-08iOS利用攝像頭獲取環(huán)境光感參數(shù)的方法
本篇文章主要介紹了iOS利用攝像頭獲取環(huán)境光感參數(shù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11iOS 獲取公歷、農(nóng)歷日期的年月日的實(shí)例代碼
本篇文章主要介紹了iOS 獲取公歷、農(nóng)歷日期的年月日的實(shí)例代碼,主要介紹了三種方法,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-02-02iOS 簡約日歷控件EBCalendarView的實(shí)現(xiàn)代碼
本篇文章主要介紹了iOS 簡約日歷控件EBCalendarView的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05iOS中的NSURLCache數(shù)據(jù)緩存類用法解析
iOS App中具體緩存操作的管理我們通常是用NSURLCache類來實(shí)現(xiàn)的,下面我們就來看一下iOS中的NSURLCache數(shù)據(jù)緩存類用法解析:2016-06-06