iOS開發中runtime介紹,ios開發runtime
一.runtime簡介
- RunTime簡稱運行時。OC就是
運行時機制
,也就是在運行時候的一些機制,其中最主要的是訊息機制。
- 對於C語言,
函數的調用在編譯的時候會決定調用哪個函數
。
- 對於OC的函數,屬於
動態調用過程
,在編譯的時候並不能決定真正調用哪個函數,只有在真正啟動並執行時候才會根據函數的名稱找到對應的函數來調用。
- 事實證明:
- 在編譯階段,OC可以
調用任何函數
,即使這個函數並未實現,只要聲明過就不會報錯。
- 在編譯階段,C語言調用
未實現的函數
就會報錯。
- 使用runtime之前需要對環境進行如下配置:Enable Strict 選擇No。
二.runtime的作用
1.發送訊息
• 方法調用的本質,就是讓對象發送訊息。
• objc_msgSend,只有對象才能發送訊息,因此以objc開頭.
• 使用訊息機制前提,必須匯入#import <objc/message.h>
• 進入檔案所在路徑,在終端使用clang -rewrite-objc main.m 指令可查看最終產生代碼。
1 // NSObject *objc = [NSObject alloc];2 NSObject *objc = objc_msgSend([NSObject class], @selector(alloc));3 4 // objc = [objc init];5 objc = objc_msgSend(objc, @selector(init));6 7 NSLog(@"%@",objc);
訊息機製作用:【調用已知的私人方法】
例:Person類中有兩個私人方法。
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 5 @end 6 7 @implementation Person 8 9 - (void)run:(NSInteger)meter10 {11 NSLog(@"跑了%ld米",meter);12 }13 14 - (void)eat15 {16 NSLog(@"吃東西");17 }18 19 @end
1 #import <objc/message.h> 2 #import "Person.h" 3 4 @interface ViewController () 5 6 @end 7 8 @implementation ViewController 9 10 - (void)viewDidLoad {11 [super viewDidLoad];12 13 //Person *p = [Person alloc];14 Person *p = objc_msgSend([Person class], @selector(alloc));15 16 //p = [p init];17 p = objc_msgSend(p, @selector(init));18 19 // 調用eat20 //[p eat];21 objc_msgSend(p, @selector(eat));22 23 // runtime24 // 方法編號後面開始,依次就是方法參數排序25 // objc_msgSend(id self, SEL op, ...)26 objc_msgSend(p, @selector(run:),20);27 }
// 調用【類方法】的方式有兩種
// 第一種通過類名調用
[Person eat];
// 第二種通過類對象調用
[[Person class] eat];
// 用類名調用類方法,底層會【自動把類名轉換成類對象調用】
// 本質:讓【類對象發送訊息】
objc_msgSend([Person class], @selector(eat));
說到這裡,不得不問:對象如何找到對應的方法去調用?
回答這個問題,首先要清楚:方法儲存到什麼地方?--->對象方法儲存到類中,類方法儲存到元類(meta class)中。每一個類都有方法列表methodList。
1.根據對象的isa指標去對應的類中尋找方法。isa:判斷去哪個類尋找對應的方法 指向方法調用的類。
2.根據傳入的方法編號(SEL),才能在方法列表中找到對應方法Method(方法名)。
3.根據方法名(函數入口)找到函數實現。
訊息機制原理:對象根據【方法編號SEL】去映射表尋找對應的方法實現。
2.交換方法
• 開發使用情境:系統內建的方法功能不能滿足需求,給系統內建的方法擴充一些功能,並且保持原有的功能。
• 方式一:繼承系統的類,重寫方法。
• 方式二:使用runtime,交換方法。
需求:給imageNamed方法提供功能,每次載入圖片就判斷片是否載入成功。
// 步驟一:先搞個分類,定義一個能載入圖片並且能列印的方法 +(UIImage *)wm_imageNamed:(NSString *)name;
// 步驟二:交換imageNamed和wm_imageNamed的實現,就能調用imageNamed,間接調用wm_imageNamed的實現。
寫一個UIImage+Image.h的分類:
1 #import <UIKit/UIKit.h>2 3 @interface UIImage (Image)4 5 // 給方法加首碼,與系統方法區分6 // 載入圖片7 + (UIImage *)wm_imageNamed:(NSString *)name;8 9 @end
1 #import "UIImage+Image.h" 2 #import <objc/message.h> 3 4 @implementation UIImage (Image) 5 6 // 載入類的時候調用,肯定只會調用一次 7 + (void)load 8 { 9 // 交換方法實現wm_imageNamed,imageNamed10 11 // 擷取方法 Method:方法名12 // 擷取類方法13 // class:擷取哪個類方法14 // SEL:方法編號15 Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));16 17 Method wm_imageNameMethod = class_getClassMethod(self, @selector(wm_imageNamed:));18 19 method_exchangeImplementations(imageNameMethod, wm_imageNameMethod);20 21 }22 23 // 載入圖片24 // 判斷25 + (UIImage *)wm_imageNamed:(NSString *)name26 {27 //這裡調用wm_imageNamed:實際上是調用imageNamed:.28 UIImage *image = [UIImage wm_imageNamed:name];29 30 if (image == nil) {31 NSLog(@"載入失敗");32 }33 34 return image;35 }36 @end
外界使用:無需匯入分類標頭檔,直接使用imageNamed:方法即可實現判斷圖片是否載入成功。
1 #import "ViewController.h" 2 3 @interface ViewController () 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad {10 [super viewDidLoad];11 12 [UIImage imageNamed:@"123"];13 }
3.動態添加方法
開發使用情境:如果一個類裡面方法非常多,載入類到記憶體的時候比較耗費資源,需要給每個方法產生映射表。可以使用動態給某個類添加方法解決。
那麼,為什麼動態添加方法?
OC大多懶載入,有些方法可能很久不會調用,節省記憶體。例如:電商,視頻,社交,收費項目:會員機制,只要會員才擁有這些功能。
在ViewController匯入Person類的標頭檔,調用run:方法。Person類並沒有run:方法的聲明和實現。
1 @implementation ViewController 2 3 - (void)viewDidLoad { 4 [super viewDidLoad]; 5 6 // _cmd:方法編號 7 NSLog(@"%@ %@",self,NSStringFromSelector(_cmd)); 8 9 Person *p = [[Person alloc] init];10 // 預設person,沒有實現run方法,可以通過performSelector調用,但是會報錯。 11 // 動態添加方法就不會報錯12 [p performSelector:@selector(run:) withObject:@20];13 14 }15 @end
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 5 @end 6 7 8 #import <objc/message.h> 9 10 @implementation Person11 12 // 定義函數13 // 沒有傳回值,有參數14 // 預設OC方法都有兩個隱式參數,self,_cmd15 void run(id self, SEL _cmd, NSNumber *meter) {16 NSLog(@"跑了%@米",meter);17 }18 19 // 什麼時候調用:當一個對象調用未實現的方法,會調用這個方法處理,並且會把對應的方法列表傳過來.20 // 作用:去解決沒有實現方法,動態添加方法21 + (BOOL)resolveInstanceMethod:(SEL)sel {22 23 // 剛好可以用來判斷,未實現的方法是不是我們想要動態添加的方法24 if (sel == @selector(run:)) {25 // 動態添加run:方法26 27 // 第一個參數class:給哪個類添加方法28 // 第二個參數SEL:添加方法的方法編號29 // 第三個參數IMP:添加方法的函數實現(函數地址)30 // 第四個參數type:函數的類型,(傳回值+參數類型) v表示void;@表示對象->self;:表示SEL->_cmd 可以傳nil31 class_addMethod(self, sel, (IMP)run, "v@:");32 33 return YES;34 }35 return [super resolveInstanceMethod:sel];36 }37 @end
class_addMethod說明,官方文檔給出:
1.函數至少要有兩個參數:self和_cmd。
2.關於第四個參數type,可以參照類型編碼Type Encodings填寫。
3.Type第二和第三個字元必須是@和:,第一個是函數傳回值類型。(實測Type傳nil也可以)
控制台列印資訊:
2016-04-15 09:52:53.634 Runtime(動態添加方法)[38020:1362250] <ViewController: 0x7f9f69d21460> viewDidLoad
2016-04-15 09:52:53.634 Runtime(動態添加方法)[38020:1362250] 跑了20米
4.給分類添加屬性
原理:給一個類聲明屬性,其實本質就是給這個類添加關聯。
屬性的本質:讓屬性與某個對象產生一段關聯
使用情境:【給系統的類添加屬性】
例:需求:給NSObject添加一個name屬性,動態添加屬性 -> runtime
建立分類NSObject+Property
1 #import <Foundation/Foundation.h> 2 3 @interface NSObject (Property) 4 5 // @property在分類中作用:僅僅是產生get,set方法聲明,必不會產生get,set方法實現和底線成員屬性 6 @property NSString *name; 7 8 @end 9 10 11 #import <objc/message.h>12 13 @implementation NSObject (Property)14 15 - (void)setName:(NSString *)name16 {17 18 // 儲存name19 // 動態添加屬性 = 本質:讓對象的某個屬性與值產生關聯20 /*21 object:儲存到哪個對象中 22 key:用什麼屬性儲存 屬性名稱23 value:儲存值24 policy:策略,strong,weak25 */26 objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);27 28 }29 30 - (NSString *)name31 {32 return objc_getAssociatedObject(self, "name");33 }34 @end分類NSObject+Property 1 #import "ViewController.h" 2 #import "Person.h" 3 #import "NSObject+Property.h" 4 5 @interface ViewController () 6 7 @end 8 9 @implementation ViewController10 11 - (void)viewDidLoad {12 [super viewDidLoad];13 14 NSObject *objc = [[NSObject alloc] init];15 16 objc.name = @"123";17 18 NSLog(@"%@",objc.name);19 }20 @end控制器ViewController
控制台列印資訊:
2016-04-15 10:19:19.100 Runtime(給分類添加屬性)[38433:1382316] 123
5.字典轉模型
由於字典轉模型內容較多,新開一個blog詳情請點擊:http://www.cnblogs.com/wm-0818/p/5394567.html