OC(Objective-C) 實(shí)現(xiàn)面向?qū)ο笕筇匦裕ǚ庋b、繼承、多態(tài))的方式,其核心在于 C語(yǔ)言 + Runtime(運(yùn)行時(shí)機(jī)制)。
下面我們來(lái)詳細(xì)拆解OC是如何實(shí)現(xiàn)面向?qū)ο蟮摹?/p>
核心思想:從 C 結(jié)構(gòu)體到 OC 對(duì)象
本質(zhì)上,每一個(gè) OC 對(duì)象(如 NSObject *obj)都是一個(gè)指向結(jié)構(gòu)體的指針。
-
封裝
-
實(shí)現(xiàn)方式:通過(guò)
@interface和@implementation來(lái)實(shí)現(xiàn)。 -
底層原理:每個(gè) Objective-C 類在編譯后,都會(huì)對(duì)應(yīng)一個(gè) C 語(yǔ)言的結(jié)構(gòu)體(struct)。這個(gè)結(jié)構(gòu)體的第一個(gè)成員通常是
Class isa,它指向類的元數(shù)據(jù)(類對(duì)象),這是運(yùn)行時(shí)機(jī)制的基石。
示例:
當(dāng)你定義一個(gè)類Person:// Person.h (接口聲明 - 封裝) @interface Person : NSObject { @private NSString *_name; // 實(shí)例變量(Ivar) } @property (nonatomic, copy) NSString *name; // 屬性(自動(dòng)生成getter/setter) - (void)sayHello; // 方法聲明 @end底層近似結(jié)構(gòu)(概念上):
struct Person_IMPL { Class isa; // 繼承自NSObject,所以第一個(gè)成員是isa NSString *_name; // 封裝在結(jié)構(gòu)體中的實(shí)例變量 };-
@public,@protected,@private關(guān)鍵字用于控制實(shí)例變量的訪問(wèn)權(quán)限,實(shí)現(xiàn)了數(shù)據(jù)隱藏。 -
@property編譯器會(huì)自動(dòng)生成對(duì)應(yīng)的getter和setter方法,這些方法就是對(duì)內(nèi)部實(shí)例變量進(jìn)行安全訪問(wèn)的接口,進(jìn)一步加強(qiáng)了封裝性。
-
實(shí)現(xiàn)方式:通過(guò)
-
繼承
-
實(shí)現(xiàn)方式:通過(guò)
:父類名的語(yǔ)法實(shí)現(xiàn)。 - 底層原理:結(jié)構(gòu)體的內(nèi)存布局。子類對(duì)應(yīng)的結(jié)構(gòu)體,其內(nèi)存布局的第一個(gè)部分就是父類的結(jié)構(gòu)體。這就是為什么子類可以訪問(wèn)父類的屬性和方法,因?yàn)閺膬?nèi)存角度看,子類對(duì)象開(kāi)頭就是一個(gè)完整的父類對(duì)象。
示例:
@interface Student : Person @property (nonatomic, copy) NSString *school; @end底層近似結(jié)構(gòu)(概念上):
struct Student_IMPL { struct Person_IMPL person_OBJ_Storage; // 本質(zhì)上就是包含一個(gè)父類結(jié)構(gòu)體 NSString *_school; };- 當(dāng)一個(gè)
Student對(duì)象被創(chuàng)建時(shí),它的內(nèi)存中不僅包含_school,也包含從Person繼承來(lái)的isa和_name。 - 方法調(diào)用時(shí),如果子類沒(méi)有實(shí)現(xiàn),就會(huì)沿著這個(gè)繼承鏈(通過(guò)
isa指針)去父類中查找。
-
實(shí)現(xiàn)方式:通過(guò)
-
多態(tài)
- 實(shí)現(xiàn)方式:多態(tài)在 OC 中主要通過(guò) 動(dòng)態(tài)類型(Dynamic Typing) 和 動(dòng)態(tài)綁定(Dynamic Binding) 來(lái)實(shí)現(xiàn),這依賴于強(qiáng)大的 Runtime 機(jī)制。
-
底層原理:
-
動(dòng)態(tài)類型:
id類型和isa指針。一個(gè)對(duì)象在運(yùn)行時(shí)才知道其真實(shí)類型。isa指針指向?qū)ο蟮念?,運(yùn)行時(shí)可以通過(guò)isa查詢到對(duì)象的實(shí)際類型。 - 動(dòng)態(tài)綁定:消息傳遞(Messaging) 機(jī)制。這是 OC 多態(tài)最核心的體現(xiàn)。
-
動(dòng)態(tài)類型:
示例:
Person *p1 = [[Person alloc] init]; Person *p2 = [[Student alloc] init]; // 多態(tài):父類指針指向子類對(duì)象 [p1 sayHello]; // 調(diào)用 Person 的 sayHello 方法 [p2 sayHello]; // 調(diào)用 Student 的 sayHello 方法(如果Student重寫了)消息傳遞的簡(jiǎn)化過(guò)程:
當(dāng)執(zhí)行[p2 sayHello]時(shí),編譯器會(huì)將其轉(zhuǎn)換為一個(gè)運(yùn)行時(shí)函數(shù)調(diào)用objc_msgSend(p2, @selector(sayHello))。-
objc_msgSend會(huì)首先找到p2指向的對(duì)象。 - 通過(guò)對(duì)象的
isa指針找到對(duì)應(yīng)的類對(duì)象Student。 - 在
Student的方法列表(method list)中查找sayHello方法。 - 如果找到,就跳轉(zhuǎn)到該方法的實(shí)現(xiàn)(函數(shù)指針)并執(zhí)行。
- 如果沒(méi)找到,就通過(guò)類對(duì)象的
super_class指針去父類Person的方法列表中查找,直到根類(NSObject)。
- 這種運(yùn)行時(shí)才決定執(zhí)行哪個(gè)方法實(shí)現(xiàn)的機(jī)制,就是動(dòng)態(tài)綁定。它允許不同的對(duì)象(
Person對(duì)象和Student對(duì)象)對(duì)同一消息(sayHello)做出不同的響應(yīng),這就是多態(tài)。
總結(jié):Runtime 是面向?qū)ο蟮囊?/h3>
| 面向?qū)ο筇匦?/th> | OC 實(shí)現(xiàn)方式 | 底層支撐 |
|---|---|---|
| 封裝 |
@interface / @implementation、@property、訪問(wèn)控制符 |
C 結(jié)構(gòu)體 |
| 繼承 |
: 父類名 語(yǔ)法 |
結(jié)構(gòu)體的內(nèi)存布局(子類結(jié)構(gòu)體包含父類結(jié)構(gòu)體) |
| 多態(tài) |
動(dòng)態(tài)類型(id, isa)和 動(dòng)態(tài)綁定(消息傳遞) |
Runtime 運(yùn)行時(shí)機(jī)制(objc_msgSend、方法列表、繼承鏈) |
關(guān)鍵結(jié)論:
Objective-C 的面向?qū)ο蟛皇怯删幾g器靜態(tài)決定的,而是由 Runtime 這個(gè)動(dòng)態(tài)系統(tǒng)在程序運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建的。類的結(jié)構(gòu)、方法的查找、消息的傳遞都是在運(yùn)行時(shí)發(fā)生的。這使得 OC 非常靈活,支持如 方法交換(Method Swizzling)、動(dòng)態(tài)添加方法/屬性 等高級(jí)特性,這也是它與 C++ 等語(yǔ)言在實(shí)現(xiàn)面向?qū)ο笊献罡镜膮^(qū)別。