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 指令可查看最終生成代碼。
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
分類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 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
控制器ViewController

控制檯打印信息:

2016-04-15 10:19:19.100 Runtime(給分類添加屬性)[38433:1382316] 123

5.字典轉模型

因爲字典轉模型內容較多,新開一個blog詳情請點擊:http://www.cnblogs.com/wm-0818/p/5394567.html

相關文章
相關標籤/搜索