深入淺出Runtime (一) 什么是Runtime? 定義?

已更新
深入淺出Runtime (二) Runtime的消息機(jī)制
深入淺出Runtime (三) Runtime的消息轉(zhuǎn)發(fā)
深入淺出Runtime (四) Runtime的實(shí)際應(yīng)用之一妓蛮,字典轉(zhuǎn)模型

深入淺出 Runtime詳解

Runtime是什么?

  • 運(yùn)行時(shí)(Runtime)是指將數(shù)據(jù)類型的確定由編譯時(shí)推遲到了運(yùn)行時(shí)
  • Runtime是一套比較底層的純C語言API, 屬于1個(gè)C語言庫, 包含了很多底層的C語言API
  • 平時(shí)編寫的OC代碼圾叼,在程序運(yùn)行過程中蛤克,其實(shí)最終會(huì)轉(zhuǎn)換成Runtime的C語言代碼,Runtime是Object-C的幕后工作者
  • Object-C需要Runtime來創(chuàng)建類和對象夷蚊,進(jìn)行消息發(fā)送和轉(zhuǎn)發(fā)

更新(上面描述并不是不對构挤,而是覺得不嚴(yán)謹(jǐn))

  • 將盡可能多的決策從編譯時(shí)和鏈接時(shí)推遲到運(yùn)行時(shí)(Apple
  • 運(yùn)行時(shí)系統(tǒng)充當(dāng)著Object-C語言的操作系統(tǒng),它使語言能夠工作(Apple)

特性: 編寫的代碼具有運(yùn)行時(shí)惕鼓、動(dòng)態(tài)特性

Runtime用來干什么筋现?用在哪些地方?

Runtime在Object-C的使用
Objective-C程序在三個(gè)不同的層次上與運(yùn)行時(shí)系統(tǒng)交互:

  • 通過Object-C源代碼進(jìn)行交互
  • 通過NSObject類中定義的方法交互
  • 通過直接調(diào)用運(yùn)行時(shí)函數(shù)

用來干什么 基本作用

  • 在程序運(yùn)行過程中箱歧,動(dòng)態(tài)的創(chuàng)建類夫否,動(dòng)態(tài)添加、修改這個(gè)類的屬性和方法叫胁;
  • 遍歷一個(gè)類中所有的成員變量凰慈、屬性、以及所有方法
  • 消息傳遞驼鹅、轉(zhuǎn)發(fā)

用在哪些地方 Runtime的典型事例

  • 給系統(tǒng)分類添加屬性微谓、方法
  • 方法交換
  • 獲取對象的屬性森篷、私有屬性
  • 字典轉(zhuǎn)換模型
  • KVC、KVO
  • 歸檔(編碼豺型、解碼)
  • NSClassFromString class<->字符串
  • block
  • 類的自我檢測
  • ...

Runtime的定義

Runtime開源代碼

在Object-C中的NSObject對象中

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

上述表述Objective-C類是由Class類型來表示的仲智,它實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

由此可見可以看到Classid 前者是類姻氨,后者指向類的指針钓辆,id是指向objc_object的一個(gè)指針,而objc_object有個(gè)isa指向objc_class的一個(gè)指針
So,不管id肴焊,還是Class最后指向的都是objc_class這個(gè)結(jié)構(gòu)體
objc_class結(jié)構(gòu)體中的定義如下:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

在runtime使用當(dāng)中前联,我們經(jīng)常需要用到的字段,它們的定義

  • isa Class對象娶眷,指向objc_class結(jié)構(gòu)體的指針似嗤,也就是這個(gè)ClassMetaClass(元類)
    - 類的實(shí)例對象的 isa 指向該類;該類的 isa 指向該類的 MetaClass
    - MetaCalss的isa對象指向RootMetaCalss
  • super_class Class對象指向父類對象
    - 如果該類的對象已經(jīng)是RootClass,那么這個(gè)super_class指向nil
    - MetaCalss的SuperClass指向父類的MetaCalss
    - MetaCalss是RootMetaCalss届宠,那么該MetaClass的SuperClass指向該對象的RootClass

一張圖可以完美的解釋這個(gè)知識(shí)點(diǎn)


  • ivars 類中所有屬性的列表烁落,使用場景:我們在字典轉(zhuǎn)換成模型的時(shí)候需要用到這個(gè)列表找到屬性的名稱,去取字典中的值豌注,KVC賦值伤塌,或者直接Runtime賦值

  • methodLists 類中所有的方法的列表,類中所有方法的列表轧铁,使用場景:如在程序中寫好方法寸谜,通過外部獲取到方法名稱字符串,然后通過這個(gè)字符串得到方法属桦,從而達(dá)到外部控制App已知方法。

  • cache 主要用于緩存常用方法列表他爸,每個(gè)類中有很多方法聂宾,我平時(shí)不用的方法也會(huì)在里面,每次運(yùn)行一個(gè)方法诊笤,都要去methodLists遍歷得到方法系谐,如果類的方法不多還行,但是基本的類中都會(huì)有很多方法讨跟,這樣勢必會(huì)影響程序的運(yùn)行效率纪他,所以cache在這里就會(huì)被用上,當(dāng)我們使用這個(gè)類的方法時(shí)先判斷cache是否為空晾匠,為空從methodLists找到調(diào)用茶袒,并保存到cache,不為空先從cache中找方法凉馆,如果找不到在去methodLists薪寓,這樣提高了程序方法的運(yùn)行效率

  • protocols 故名思義亡资,這個(gè)類中都遵守了哪些協(xié)議,使用場景:判斷類是否遵守了某個(gè)協(xié)議上

深入Runtime的運(yùn)行原理

當(dāng)我寫到深入Runtime的運(yùn)行原理的時(shí)候,腦海中冒出的想法是怎么深入向叉,從哪里開始挖掘runtime的內(nèi)容:

第一個(gè)想法就是

  • 介紹runtime的幾個(gè)方法
  • runtime的使用方法
  • runtime的實(shí)際操作場景锥腻、應(yīng)用
  • runtime的總結(jié)

如果說這些就是深入runtime,就是調(diào)用下api

然后想了很久母谎,每次都會(huì)想就直接介紹使用瘦黑,總結(jié)完事;心里賊不得勁
不能就這么簡簡單單了事奇唤,那么開始想到哪點(diǎn)幸斥,深入做哪點(diǎn)深入

大綱(后續(xù)會(huì)補(bǔ)充):

  • 類底層代碼、類的本質(zhì)冻记?
  • 類底層是如何調(diào)用方法睡毒?
  • Runtime消息傳遞
  • Runtime消息轉(zhuǎn)發(fā)
  • Runtime起到了什么作用?
  • Runtime實(shí)際應(yīng)用

類底層代碼冗栗、類的本質(zhì)演顾?

為了更好的認(rèn)識(shí)類是怎么工作的,我們將要將一段Object-C的代碼用clang看下底層的C/C++的寫法

typedef enum : NSUInteger {
    ThisRPGGame = 0,
    ThisActionGame = 1,
    ThisBattleFlagGame = 2,
} ThisGameType;


@interface Game : NSObject
@property (copy,nonatomic)NSString *Name;
@property (assign,nonatomic)ThisGameType Type;
@end

@implementation Game
@synthesize Name,Type;

- (void)GiveThisGameName:(NSString *)name{
    Name = name;
}

- (void)GiveThisGameType:(ThisGameType)type{
    Type = type;
}

@end

使用命令隅居,在當(dāng)前文件夾中會(huì)出現(xiàn)Game.cpp的文件

# clang -rewrite-objc Game.m

Game.cpp
由于生成的文件很龐大钠至,可以仔細(xì)去研讀,受益匪淺

研讀方式:如果按照從上往下的順序去研讀胎源,會(huì)很不理解棉钧,所以我的研讀方式從關(guān)鍵點(diǎn)切入首先理解關(guān)鍵的幾點(diǎn),然后在慢慢拋析

/*
 * 顧名思義存放property的結(jié)構(gòu)體
 * 當(dāng)我們使用perproty的時(shí)候涕蚤,會(huì)生成這樣一個(gè)結(jié)構(gòu)體
 * 具體存儲(chǔ)的數(shù)據(jù)為 
 * 實(shí)際內(nèi)容:"Name","T@\"NSString\",C,N,VName" 
 * 原型:@property (copy,nonatomic)NSString *Name;
 * 這個(gè)具體是怎么實(shí)現(xiàn)的宪卿,我會(huì)在后面繼續(xù)深入研究,本文主要來理解runtime的理解
 **/
struct _prop_t {
    const char *name;        //名字
    const char *attributes;  //屬性
};

/*
 *類中方法的結(jié)構(gòu)體,cmd和imp的關(guān)系是一一對應(yīng)的關(guān)系
 *創(chuàng)建對象生成isa指針万栅,指向這個(gè)對象的結(jié)構(gòu)體時(shí) 
 *同時(shí)生成了一個(gè)表"Dispatch table"通過這個(gè)_cmd的編號找到對應(yīng)方法
 *使用場景:
 *例如方法交換佑钾,方法判斷。烦粒。休溶。
 **/ 
struct _objc_method {
    struct objc_selector * _cmd;   //SEL 對應(yīng)著OC中的@selector()
    const char *method_type;       //方法的類型
    void  *_imp;                   //方法的地址
}; 


/*
 * method_list_t 結(jié)構(gòu)體:
 * 原型:
 * - (void)GiveThisGameName:(NSString *)name;
 * 實(shí)際存儲(chǔ)的方式:
 * {(struct objc_selector *)"GiveThisGameName:", "v24@0:8@16", (void *)_I_Game_GiveThisGameName_}
 * 其主要目的是存儲(chǔ)一個(gè)數(shù)組扰她,基本的數(shù)據(jù)類型是 _objc_method
 * 擴(kuò)展:當(dāng)然這其中有你的屬性兽掰,自動(dòng)生成的setter、getter方法
 **/
 
static struct _method_list_t {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[6];
}

/*
 * 表示這個(gè)類中所遵守的協(xié)議對象
 * 使用場景:
 * 判斷類是否遵守這個(gè)協(xié)議徒役,從而動(dòng)態(tài)添加孽尽、重寫、交換某些方法忧勿,來達(dá)到某些目的
 * 
 **/

struct _protocol_t {
    void * isa;  // NULL
    const char *protocol_name;
    const struct _protocol_list_t * protocol_list; // super protocols
    const struct method_list_t *instance_methods;  // 實(shí)例方法
    const struct method_list_t *class_methods;     //類方法
    const struct method_list_t *optionalInstanceMethods;  //可選的實(shí)例方法
    const struct method_list_t *optionalClassMethods;  //可選的類方法
    const struct _prop_list_t * properties;  //屬性列表
    const unsigned int size;  // sizeof(struct _protocol_t)
    const unsigned int flags;  // = 0
    const char ** extendedMethodTypes;  //擴(kuò)展的方法類型
};

/*
 * 類的變量的結(jié)構(gòu)體
 * 原型:
 * NSString *Name;
 * 存儲(chǔ)內(nèi)容:
 * {(unsigned long int *)&OBJC_IVAR_$_Game$Name, "Name", "@\"NSString\"", 3, 8}
 * 根據(jù)存儲(chǔ)內(nèi)容可以大概了解這些屬性的工作內(nèi)容
 **/
struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;  //名字
    const char *type;  //屬于什么變量
    unsigned int alignment; //未知
    unsigned int  size;    //大小
};


/*
 *  這個(gè)就是類中的各種方法泻云、屬性艇拍、等等信息
 *  底層也是一個(gè)結(jié)構(gòu)體
 *  名稱、方法列表宠纯、協(xié)議列表卸夕、變量列表、layout婆瓜、properties快集。。
 *  
 **/
struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    unsigned int reserved;
    const unsigned char *ivarLayout;  //布局
    const char *name;  //名字
    const struct _method_list_t *baseMethods;//方法列表 
    const struct _objc_protocol_list *baseProtocols; //協(xié)議列表
    const struct _ivar_list_t *ivars;  //變量列表
    const unsigned char *weakIvarLayout;  //弱引用布局
    const struct _prop_list_t *properties;  //屬性列表
};

/*
 * 類本身
 * oc在創(chuàng)建類的時(shí)候都會(huì)創(chuàng)建一個(gè) _class_t的結(jié)構(gòu)體
 * 我的理解是在runtime中的object-class結(jié)構(gòu)體在底層就會(huì)變成_class_t結(jié)構(gòu)體
 **/
struct _class_t {
    struct _class_t *isa;  //元類的指針
    struct _class_t *superclass; //父類的指針
    void *cache;   //緩存
    void *vtable;  //表信息廉白、未知
    struct _class_ro_t *ro;  //這個(gè)就是類中的各種方法个初、屬性、等等信息
};


/*
 * 類擴(kuò)展的結(jié)構(gòu)體
 * 在OC中寫的分類
 **/
struct _category_t {
    const char *name;  //名稱
    struct _class_t *cls;  //這個(gè)是哪個(gè)類的擴(kuò)展
    const struct _method_list_t *instance_methods;  //實(shí)例方法列表
    const struct _method_list_t *class_methods;     //類方法列表
    const struct _protocol_list_t *protocols;       //協(xié)議列表
    const struct _prop_list_t *properties;          //屬性列表
};

上述是Object-C中類中基本的數(shù)據(jù)猴蹂,了解了類的定義院溺,我們基本可以這么理解,類就是多個(gè)結(jié)構(gòu)體組合的一個(gè)集合體磅轻,類中的行為珍逸、習(xí)慣、屬性抽象聋溜,按照機(jī)器能懂的數(shù)據(jù)存儲(chǔ)到我們底層的結(jié)構(gòu)體當(dāng)中谆膳,在我們需要使用的時(shí)候直接獲取使用。

那么就開始研究一下撮躁,類是如何使用漱病,類的基本使用過程以及過程中runtime所做的事情。

類底層是如何調(diào)用方法把曼?

了解了類的組成杨帽,那么類是通過什么樣的形式去獲取方法屬性并得到應(yīng)用?
在Object-C開發(fā)中我們經(jīng)常會(huì)說到,對象調(diào)用方法嗤军,其本質(zhì)就是想這個(gè)對象發(fā)送消息注盈,為什么會(huì)有這么一說?下面我們來驗(yàn)證一下

Object-C代碼

int main(int argc, char * argv[]) {

    Game *game = [Game alloc];
    [game init];
    [game Play];
    return  0;
}

底層代碼的實(shí)現(xiàn)

int main(int argc, char * argv[]) {

    Game *game = ((Game *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Game"), sel_registerName("alloc"));
    game = ((Game *(*)(id, SEL))(void *)objc_msgSend)((id)game, sel_registerName("init"));
    ((void (*)(id, SEL))(void *)objc_msgSend)((id)game, sel_registerName("Play"));
    return 0;
}

代碼中使用了

  • objc_msgSend 消息發(fā)送
  • objc_getClass 獲取對象
  • sel_registerName 獲取方法的SEL

因?yàn)槟壳爸攸c(diǎn)是objc_msgSend型雳,其他的Runtime的方法會(huì)在后面繼續(xù)一一道來, So 一個(gè)對象調(diào)用其方法,在Object-C中就是向這個(gè)對象發(fā)送一條消息山害,消息的格式

objc_msgSend("對象","SEL","參數(shù)"...)
objc_msgSend( id self, SEL op, ... )

總結(jié)

Rumtime是Objective-C語言動(dòng)態(tài)的核心纠俭,Objective-C的對象一般都是基于Runtime的類結(jié)構(gòu),達(dá)到很多在編譯時(shí)確定方法推遲到了運(yùn)行時(shí)浪慌,從而達(dá)到動(dòng)態(tài)修改冤荆、確定、交換...屬性及方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末权纤,一起剝皮案震驚了整個(gè)濱河市钓简,隨后出現(xiàn)的幾起案子乌妒,更是在濱河造成了極大的恐慌,老刑警劉巖外邓,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤蚊,死亡現(xiàn)場離奇詭異,居然都是意外死亡损话,警方通過查閱死者的電腦和手機(jī)侦啸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丧枪,“玉大人光涂,你說我怎么就攤上這事∨》常” “怎么了忘闻?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恋博。 經(jīng)常有香客問我齐佳,道長,這世上最難降的妖魔是什么交播? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任重虑,我火速辦了婚禮,結(jié)果婚禮上秦士,老公的妹妹穿的比我還像新娘缺厉。我一直安慰自己,他們只是感情好隧土,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布提针。 她就那樣靜靜地躺著,像睡著了一般曹傀。 火紅的嫁衣襯著肌膚如雪辐脖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天皆愉,我揣著相機(jī)與錄音嗜价,去河邊找鬼。 笑死幕庐,一個(gè)胖子當(dāng)著我的面吹牛久锥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播异剥,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼瑟由,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了冤寿?” 一聲冷哼從身側(cè)響起歹苦,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤青伤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后殴瘦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狠角,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年痴施,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了擎厢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辣吃,死狀恐怖动遭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情神得,我是刑警寧澤厘惦,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站哩簿,受9級特大地震影響宵蕉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜节榜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一羡玛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宗苍,春花似錦稼稿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丽啡,卻和暖如春谋右,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背补箍。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工改执, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坑雅。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓辈挂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親霞丧。 傳聞我的和親對象是個(gè)殘疾皇子呢岗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容