運行時機制
,也就是在運行時候的一些機制,其中最主要的是消息機制。函數的調用在編譯的時候會決定調用哪一個函數
。動態調用過程
,在編譯的時候並不能決定真正調用哪一個函數,只有在真正運行的時候纔會根據函數的名稱找到對應的函數來調用。調用任何函數
,即便這個函數並未實現,只要聲明過就不會報錯。未實現的函數
就會報錯。1.發送消息
• 方法調用的本質,就是讓對象發送消息。
• objc_msgSend,只有對象才能發送消息,所以以objc開頭.
• 使用消息機制前提,必須導入#import <objc/message.h>
• 進入文件所在路徑,在終端使用clang -rewrite-objc main.m 指令可查看最終生成代碼。html
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);
消息機制做用:【調用已知的私有方法】ide
例:Person類中有兩個私有方法。函數
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 5 @end 6 7 @implementation Person 8 9 - (void)run:(NSInteger)meter 10 { 11 NSLog(@"跑了%ld米",meter); 12 } 13 14 - (void)eat 15 { 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 // 調用eat 20 //[p eat]; 21 objc_msgSend(p, @selector(eat)); 22 23 // runtime 24 // 方法編號後面開始,依次就是方法參數排序 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.根據方法名(函數入口)找到函數實現。spa
消息機制原理:對象根據【方法編號SEL】去映射表查找對應的方法實現。指針
2.交換方法code
• 開發使用場景:系統自帶的方法功能不能知足需求,給系統自帶的方法擴展一些功能,而且保持原有的功能。
• 方式一:繼承系統的類,重寫方法。
• 方式二:使用runtime,交換方法。orm
需求:給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,imageNamed 10 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 *)name 26 { 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 Person 11 12 // 定義函數 13 // 沒有返回值,有參數 14 // 默認OC方法都有兩個隱式參數,self,_cmd 15 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 能夠傳nil 31 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 *)name 16 { 17 18 // 保存name 19 // 動態添加屬性 = 本質:讓對象的某個屬性與值產生關聯 20 /* 21 object:保存到哪一個對象中 22 key:用什麼屬性保存 屬性名 23 value:保存值 24 policy:策略,strong,weak 25 */ 26 objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 28 } 29 30 - (NSString *)name 31 { 32 return objc_getAssociatedObject(self, "name"); 33 } 34 @end
1 #import "ViewController.h" 2 #import "Person.h" 3 #import "NSObject+Property.h" 4 5 @interface ViewController () 6 7 @end 8 9 @implementation ViewController 10 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
控制檯打印信息:
2016-04-15 10:19:19.100 Runtime(給分類添加屬性)[38433:1382316] 123
5.字典轉模型
因爲字典轉模型內容較多,新開一個blog詳情請點擊:http://www.cnblogs.com/wm-0818/p/5394567.html