RunTime原理之一:如何讓Objective-C支持面向?qū)ο蟮哪芰δ兀?/h1>

一挥萌、一切從面向?qū)ο箝_始說起

OC是一門基于C的面向?qū)ο蟮恼Z言,這是如何做到的呢妒蛇?下面我們簡單分析窺探下這背后的密碼官紫。

「面向?qū)ο蟆褂心男┨匦阅兀?/h5>
  • 最基本的概念有驼鹅,類、對象(類實(shí)例)照皆、成員變量重绷、方法。
  • OC特有的一些性質(zhì)膜毁,屬性昭卓、協(xié)議愤钾、類別/擴(kuò)展、

二候醒、循序漸進(jìn)深入「面向?qū)ο蟆垢鞣N「概念」的封裝

1能颁、類(關(guān)鍵字Class

/// 類
typedef struct objc_class *Class;

struct objc_class {
    Class isa;                                // 實(shí)現(xiàn)方法調(diào)用的關(guān)鍵
    Class super_class;                        // 父類
    const char * name;                        // 類名
    long version;                             // 類的版本信息,默認(rèn)為0
    long info;                                // 類信息倒淫,供運(yùn)行期使用的一些位標(biāo)識
    long instance_size;                       // 該類的實(shí)例變量大小
    struct objc_ivar_list * ivars;            // 該類的成員變量鏈表
    struct objc_method_list ** methodLists;   // 方法定義的鏈表
    struct objc_cache * cache;                // 方法緩存
    struct objc_protocol_list * protocols;    // 協(xié)議鏈表
};

其中關(guān)鍵字段:ivars(屬性鏈表)伙菊、methodLists(「類實(shí)例方法」鏈表)、cache(方法緩存)敌土、protocols(協(xié)議鏈表)镜硕、isa(指向類的元類)。

OC設(shè)計(jì)者返干,將設(shè)計(jì)成一個對象兴枯!從struct objc_class里面有個isa指針,可以推斷出這點(diǎn)來矩欠。那么又是誰的對象呢财剖?其實(shí)是「元類」,關(guān)于它將在第二章中說明癌淮。

2躺坟、對象(關(guān)鍵字id

/// 對象
struct objc_object {
    Class isa;
};

/// id指針
typedef struct objc_object *id;

struct objc_objectisa 指向的是「對象」的「類」;id實(shí)質(zhì) struct objc_object *指針乳蓄,因此其可以指向任何對象咪橙。
例如NSObject *obj = [[NSObject alloc] init];,對象 obj 離的 isa指針 指向就是NSObject栓袖。

3匣摘、方法(關(guān)鍵字SEL IMP

OC利用 @ selector 可以獲取方法店诗,其返回的是SEL裹刮,如SEL func = @selector(viewWillAppear:); 那么Runtime又是怎么封裝的呢?
還是 struct objc_class 里面的方法列表 struct objc_method_list ** methodLists; 說起庞瘸,看看結(jié)構(gòu)體struct objc_method_list 是怎么定義的捧弃。

// 方法列表
struct objc_method_list {
    struct objc_method_list *obsolete ;

    int method_count;

    /* variable length structure */
    struct objc_method method_list[1];
}

// Method
typedef struct objc_method *Method;
struct objc_method {
    SEL method_name;
    char * method_types;
    IMP method_imp;
};

// SEL
typedef struct objc_selector *SEL;

// IMP
typedef id (*IMP)(id, SEL, ...); 
  • SEL 是一個指向 objc_selector 結(jié)構(gòu)體的指針,而Runtime中沒有詳細(xì)的objc_selector的定義擦囊。
// 通過打印測試
SEL sel = @selector(viewWillAppear:);
NSLog(@"%s", sel);          // 輸出:viewWillAppear:

推斷而知违霞,SEL其實(shí)就是字符串表示方法的名稱。

  • IMP瞬场,從定義而知买鸽,其實(shí)質(zhì)是一個函數(shù)指針,所指向的就是方法的實(shí)現(xiàn)贯被。
    IMP = id + SEL + ...(即參數(shù)列表)眼五。其中SEL就是方法名妆艘;參數(shù)列表就是方法參數(shù);id指向是self看幼,對于實(shí)例方法來說批旺, self 保存了當(dāng)前對象的地址;對于類方法來說诵姜, self 保存了當(dāng)前對應(yīng)類對象的地址汽煮。

也可以從NSObject提供的方法- (IMP)methodForSelector:(SEL)aSelector;+ (IMP)instanceMethodForSelector:(SEL)aSelector; 獲取到「方法」的具體「實(shí)現(xiàn)IMP」。

  • method_types棚唆,方法類型暇赤,用來存儲方法的參數(shù)類型和返回值類型。

4宵凌、成員變量(關(guān)鍵字Ivar

類結(jié)構(gòu)體 struct objc_class 里面有一個成員變量列表 struct objc_ivar_list * ivars;翎卓,保存「類」的所有成員變量,它對應(yīng)關(guān)鍵字是Ivar摆寄,它的結(jié)構(gòu)體是struct objc_ivar失暴。

// 成員變量列表
struct objc_ivar_list {
    int ivar_count;

    /* variable length structure */
    struct objc_ivar ivar_list[1];
};

typedef struct objc_ivar *Ivar;
struct objc_ivar {
    char *ivar_name; // 變量名稱
    char *ivar_type; // 變量類型名
    int ivar_offset; // 基地址偏移字節(jié)
}

結(jié)構(gòu)體 struct objc_ivar 保存了「成員變量」的名稱、類型微饥,以及對應(yīng)的偏移地址逗扒。

5、協(xié)議欠橘,Category

typedef struct category_t *Category;

struct category_t {
    const char *name;                                // 類名
    classref_t cls;                                  // 類矩肩,在運(yùn)行時(shí)階段通過 clasee_name(類名)對應(yīng)到類對象
    struct method_list_t *instanceMethods;           // Category 中所有添加的對象方法列表
    struct method_list_t *classMethods;              // Category 中所有添加的類方法列表
    struct protocol_list_t *protocols;               // Category 中實(shí)現(xiàn)的所有協(xié)議列表
    struct property_list_t *instanceProperties;      // Category 中添加的所有屬性
};

Category 的結(jié)構(gòu)體定義中也可以看出, Category 可以為類添加對象方法肃续、類方法黍檩、協(xié)議、屬性始锚。但是 Category 無法添加「成員變量」刽酱。

  • categoryextension的區(qū)別
    extension像是一個匿名的category,但是extension是在編譯期決議瞧捌,它就是類的一部分棵里;而category是在運(yùn)行期決議的。
    extension一般用來隱藏類的私有信息姐呐,必須有一個類的源碼才能為一個類添加extension殿怜;而category則不需要。

    extension可以添加實(shí)例變量曙砂,而category是無法添加實(shí)例變量的头谜。(這是因?yàn)?code>category是在運(yùn)行期決議,而在運(yùn)行期的時(shí)候?qū)ο蟮膬?nèi)存布局已經(jīng)確定鸠澈,如果添加實(shí)例變量就會破壞類的內(nèi)部布局柱告,這對編譯型語言來說是災(zāi)難性的)砖织。

6、屬性和屬性特性(關(guān)鍵字Property

// 屬性
typedef struct objc_property *Property;
typedef struct objc_property *objc_property_t;

// 屬性特性
typedef struct {
    const char * _Nonnull name;           /**< The name of the attribute */
    const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

通過 clang 將OC代碼轉(zhuǎn)寫為.cpp的相關(guān)代碼可知末荐,本質(zhì)上 結(jié)構(gòu)體struct objc_property 其實(shí)就是 結(jié)構(gòu)體struct _prop_t侧纯,其大致結(jié)構(gòu)如下:

struct _prop_t {
    const char *name;  //名稱
    const char *attributes;  // readonly、assign的修飾屬性
};
成員變量與屬性的聯(lián)系

本質(zhì)上甲脏,一個屬性一定對應(yīng)一個成員變量眶熬。但是屬性又不僅僅是一個成員變量,屬性還會根據(jù)自己對應(yīng)的屬性特性的定義來對這個成員變量進(jìn)行一系列的封裝:提供 Getter/Setter 方法块请、內(nèi)存管理策略娜氏、線程安全機(jī)制等等。

7墩新、協(xié)議(關(guān)鍵字Protocol

struct objc_protocol_list {
    struct objc_protocol_list * _Nullable next;
    long count;
    __unsafe_unretained Protocol * _Nullable list[1];
};

// 宏__OBJC__保證只有OC文件可以調(diào)用.h里面的頭文件贸弥,一些非OC語言不能調(diào)用
#ifdef __OBJC__  
@class Protocol;  // OC當(dāng)做類
#else
typedef struct objc_object Protocol; 
#endif

實(shí)質(zhì)上 Protocol 對應(yīng)的結(jié)構(gòu)體 struct objc_object,內(nèi)部只有一個isa指針海渊,指向?qū)?yīng)的類绵疲。

8、cache

typedef struct objc_cache *Cache

struct objc_cache {
    unsigned int mask /* total = mask + 1 */  ;
    unsigned int occupied;
    Method buckets[1];
};

方法調(diào)用的時(shí)候臣疑,會現(xiàn)在緩存中查找盔憨,找到返回,否則再去方法列表中找讯沈。這樣設(shè)計(jì)的目的在于提高方法調(diào)用效率郁岩。

三、總結(jié)

上面學(xué)習(xí)了Runtime對于面向?qū)ο蟾拍畹囊恍┓庋b缺狠,其實(shí)質(zhì)用C語言的結(jié)構(gòu)體问慎、指針、函數(shù)指針等挤茄;大量使用鏈表如叼、map等作為存儲;設(shè)計(jì)算法職稱驮樊,如緩存薇正,讓Objective-C具有面向?qū)ο蟮哪芰Α?/p>

Runtime針對上面的每一個結(jié)構(gòu)體,都提供豐富API接口囚衔,允許對其進(jìn)行操作。也正是如此設(shè)計(jì)雕沿,讓Objective-C能在運(yùn)行時(shí)练湿,獲取、創(chuàng)建审轮、修改類相關(guān)的數(shù)據(jù)肥哎,具備強(qiáng)大的動態(tài)能力辽俗。

后面章節(jié),我們將繼續(xù)學(xué)習(xí)篡诽,Runtime是如何利用這些結(jié)構(gòu)體實(shí)現(xiàn)面向?qū)ο蟮哪芰Φ摹?/p>

其他

新手也看得懂的 iOS Runtime 教程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末崖飘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杈女,更是在濱河造成了極大的恐慌朱浴,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件达椰,死亡現(xiàn)場離奇詭異翰蠢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)啰劲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進(jìn)店門梁沧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蝇裤,你說我怎么就攤上這事廷支。” “怎么了栓辜?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵酥泞,是天一觀的道長。 經(jīng)常有香客問我啃憎,道長芝囤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任辛萍,我火速辦了婚禮悯姊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贩毕。我一直安慰自己悯许,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布辉阶。 她就那樣靜靜地躺著先壕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谆甜。 梳的紋絲不亂的頭發(fā)上垃僚,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天,我揣著相機(jī)與錄音规辱,去河邊找鬼谆棺。 笑死,一個胖子當(dāng)著我的面吹牛罕袋,可吹牛的內(nèi)容都是我干的改淑。 我是一名探鬼主播碍岔,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼朵夏!你這毒婦竟也來了蔼啦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤仰猖,失蹤者是張志新(化名)和其女友劉穎捏肢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亮元,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猛计,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爆捞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奉瘤。...
    茶點(diǎn)故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖煮甥,靈堂內(nèi)的尸體忽然破棺而出盗温,到底是詐尸還是另有隱情,我是刑警寧澤成肘,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布卖局,位于F島的核電站,受9級特大地震影響双霍,放射性物質(zhì)發(fā)生泄漏砚偶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一洒闸、第九天 我趴在偏房一處隱蔽的房頂上張望染坯。 院中可真熱鬧,春花似錦丘逸、人聲如沸单鹿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仲锄。三九已至,卻和暖如春湃鹊,著一層夾襖步出監(jiān)牢的瞬間儒喊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工涛舍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澄惊,地道東北人。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓富雅,卻偏偏與公主長得像掸驱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子没佑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評論 2 361