一挥萌、一切從面向?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é)議鏈表
};
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_object
的 isa
指向的是「對象」的「類」;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
無法添加「成員變量」刽酱。
-
category
和extension
的區(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>