iOS Class結構體分析~詳解

//聯系人:石虎QQ: 1224614774昵稱:嗡嘛呢叭咪哄

objc_class結構體

一、類在OC中是objc_class的結構體指針typedef struct objc_class *Class;

在objc/runtime.h中objc_class結構體的定義如下:

struct objc_class {

Class isa? OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

Class super_class ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;//父類

const char *name? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;//類名

long version ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;//類的版本信息,默認為0

long info ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;//類信息,供運行期使用的一些位標識

long instance_size? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;//該類的實例變量大小

struct objc_ivar_list *ivars ? ? ? ? ? OBJC2_UNAVAILABLE;//該類的成員變量鏈表

struct objc_method_list **methodLists ? OBJC2_UNAVAILABLE;//方法定義的鏈表

struct objc_cache *cache ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;//方法緩存

struct objc_protocol_list *protocols ? ? ? ?OBJC2_UNAVAILABLE;//協議鏈表

#endif

} OBJC2_UNAVAILABLE;

isa:指向元類的objc_class結構體指針,iOS中的類也是對象,元類中儲存有類對象的類方法;

superclass:指向父類的objc_class結構體指針,可以通過父類的指針找到變量和方法;

name:類名;

version:版本號,默認為0

info:其他信息,運行期間的一些位標示

instance_size:類實例變量大小

ivars:該類的成員變量鏈表,是objc_ivar_list結構體指針,

[objc]view plaincopy

structobjc_ivar_list?{

intivar_count;

/*?variable?length?structure?*/

structobjc_ivar?ivar_list[1];

}

objc_var:變量結構體---名稱,類型,偏移字節和占用的空間

[objc]view plaincopy

structobjc_ivar?{

charchar*ivar_name??????????????????????????????????????????OBJC2_UNAVAILABLE;

charchar*ivar_type??????????????????????????????????????????OBJC2_UNAVAILABLE;

intivar_offset ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;

#ifdef?__LP64__

intspace ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;

#endif

}

objc_method_list:方法鏈表結構體

[objc]view plaincopy

structobjc_method_list?{

structobjc_method_list*obsolete????????????????????????OBJC2_UNAVAILABLE;

intmethod_count?????????????????????????????????????????OBJC2_UNAVAILABLE;

#ifdef?__LP64__

intspace????????????????????????????????????????????????OBJC2_UNAVAILABLE;

#endif

/*?variable?length?structure?*/

structobjc_method?method_list[1]????????????????????????OBJC2_UNAVAILABLE;

}

method:對象的每個方法的結構體,SEL是方法選擇器,是HASH后的值,可以通過這個值找到函數體的實現,IMP 是函數指針

[objc]view plaincopy

structobjc_method?{

SELmethod_name??????????????????????????????????????????OBJC2_UNAVAILABLE;

charchar*method_types ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

IMP?method_imp???????????????????????????????????????????OBJC2_UNAVAILABLE;

}

cache:對象使用過的方法鏈表,

[objc]view plaincopy

structobjc_cache?{

unsignedintmask/*?total?=?mask?+?1?*/OBJC2_UNAVAILABLE;

unsignedintoccupied????????????????????????????????????OBJC2_UNAVAILABLE;

Method?buckets[1]????????????????????????????????????????OBJC2_UNAVAILABLE;

};

protocols:協議鏈表

[objc]view plaincopy

structobjc_protocol_list?{

structobjc_protocol_list*next;

longcount;

Protocol*list[1];

};

二、objc_class的定義

我們在使用runtime以class為前綴的方法時主要就是針對這個結構體中的各個字段的。

指向元類的指針(isa)

在OC中所有的類其實也是一個對象,那么這個對象也會有一個所屬的類,這個類就是元類也就是結構體里面isa指針所指的類。

那什么是元類呢?

元類的定義:元類就是類對象的類。每個類都有自己的元類,因為每個類都有自己獨一無二的方法。

簡單點說就是:

當你給對象發送消息時,消息是在尋找這個對象的類的方法列表。(實例方法)

當你給類發消息時,消息是在尋找這個類的元類的方法列表。(類方法)

那元類的類是什么呢?

元類,就像之前的類一樣,它也是一個對象。你也可以調用它的方法。自然的,這就意味著他必須也有一個類。

所有的元類都使用根元類(繼承體系中處于頂端的類的元類)作為他們的類。這就意味著所有NSObject的子類(大多數類)的元類都會以NSObject的元類作為他們的類

根據這個規則,所有的元類使用根元類作為他們的類,根元類的元類則就是它自己。也就是說基類的元類的isa指針指向他自己。

這里有一副圖可以很好的展現這些關系:

三、runtime方法

//判斷給定的Class是否是一個元類

BOOL class_isMetaClass(Class cls);

class_isMetaClass函數,如果是cls是元類,則返回YES;如果否或者傳入的cls為Nil,則返回NO。

指向父類的指針(super_class)

指向該類的父類,如果該類已經是最頂層的根類(如NSObject或NSProxy),則super_class為NULL。

//獲取類的父類

Class class_getSuperclass(Class cls);

class_getSuperclass函數,當cls為Nil或者cls為根類時,返回Nil。不過通常我們可以使用NSObject類的superclass方法來達到同樣的目的。

類名(name)

//獲取類的類名

const char * class_getName(Class cls);

對于class_getName函數,如果傳入的cls為Nil,則返回一個字字符串。

版本(version)

版本相關的操作包含以下函數:

//獲取版本號

int class_getVersion(Class cls);

//設置版本號

void class_setVersion(Class cls,int version);

實例變量大小(instance_size)

//獲取實例大小

size_t class_getInstanceSize(Class cls);

成員變量(ivars)及屬性

在objc_class中,所有的成員變量、屬性的信息是放在鏈表ivars中的。ivars是一個數組,數組中每個元素是指向Ivar(變量信息)的指針。runtime提供了豐富的函數來操作這一字段。大體上可以分為以下幾類:

1.成員變量操作函數,主要包含以下函數:

//獲取類中指定名稱實例成員變量的信息

Ivar class_getInstanceVariable(Class cls,const char *name);

//獲取類成員變量的信息

Ivar class_getClassVariable(Class cls,const char *name);

//添加成員變量

BOOL class_addIvar(Class cls,const char *name,size_t size,uint8_t alignment,const char *types);

//獲取整個成員變量列表

Ivar * class_copyIvarList(Class cls,unsigned int *outCount);

class_getInstanceVariable函數,它返回一個指向包含name指定的成員變量信息的objc_ivar結構體的指針(Ivar)。

class_getClassVariable函數,目前沒有找到關于Objective-C中類變量的信息,一般認為Objective-C不支持類變量。注意,返回的列表不包含父類的成員變量和屬性。

Objective-C不支持往已存在的類中添加實例變量,因此不管是系統庫提供的提供的類,還是我們自定義的類,都無法動態添加成員變量。但如果我們通過運行時來創建一個類的話,又應該如何給它添加成員變量呢?這時我們就可以使用class_addIvar函數了。不過需要注意的是,這個方法只能在objc_allocateClassPair函數與objc_registerClassPair之間調用。另外,這個類也不能是元類。成員變量的按字節最小對齊量是1<

class_copyIvarList函數,它返回一個指向成員變量信息的數組,數組中每個元素是指向該成員變量信息的objc_ivar結構體的指針。這個數組不包含在父類中聲明的變量。outCount指針返回數組的大小。需要注意的是,我們必須使用free()來釋放這個數組。

2.屬性操作函數,主要包含以下函數:

//獲取指定的屬性

objc_property_t class_getProperty(Class cls,const char *name);

//獲取屬性列表

objc_property_t * class_copyPropertyList(Class cls,unsigned int *outCount);

//為類添加屬性

BOOL class_addProperty(Class cls,const char *name,const objc_property_attribute_t *attributes,unsigned int attributeCount);

//替換類的屬性

void class_replaceProperty(Class cls,const char *name,const objc_property_attribute_t *attributes,unsigned int attributeCount);

這一種方法也是針對ivars來操作,不過只操作那些是屬性的值。

方法(methodLists)

objc_method_list方法鏈表中存放的是該類的成員方法(-方法),類方法(+方法)存在meta-class的objc_method_list鏈表中。

方法操作主要有以下函數:

//添加方法

BOOL class_addMethod(Class cls,SEL name,IMP imp,const char *types);

//獲取實例方法

Method class_getInstanceMethod(Class cls,SEL name);

//獲取類方法

Method class_getClassMethod(Class cls,SEL name);

//獲取所有方法的數組

Method * class_copyMethodList(Class cls,unsigned int *outCount);

//替代方法的實現

IMP class_replaceMethod(Class cls,SEL name,IMP imp,const char *types);

//返回方法的具體實現

IMP class_getMethodImplementation(Class cls,SEL name);

IMP class_getMethodImplementation_stret(Class cls,SEL name);

//類實例是否響應指定的selector

BOOL class_respondsToSelector(Class cls,SEL sel);

class_addMethod的實現會覆蓋父類的方法實現,但不會取代本類中已存在的實現,如果本類中包含一個同名的實現,則函數會返回NO。如果要修改已存在實現,可以使用method_setImplementation。一個Objective-C方法是一個簡單的C函數,它至少包含兩個參數—self和_cmd。所以,我們的實現函數(IMP參數指向的函數)至少需要兩個參數,如下所示:

void myMethodIMP(id self,SEL _cmd)

{

// implementation ....

}

與成員變量不同的是,我們可以為類動態添加方法,不管這個類是否已存在。

另外,參數types是一個描述傳遞給方法的參數類型的字符數組,這就涉及到類型編碼,我們將在后面介紹。

這里我們的void的前面沒有+、-號,因為只是C的代碼。

class_getInstanceMethod、class_getClassMethod函數,與class_copyMethodList不同的是,這兩個函數都會去搜索父類的實現。

class_copyMethodList函數,返回包含所有實例方法的數組,如果需要獲取類方法,則可以使用class_copyMethodList(object_getClass(cls),&count)(一個類的實例方法是定義在元類里面)。該列表不包含父類實現的方法。outCount參數返回方法的個數。在獲取到列表后,我們需要使用free()方法來釋放它。

class_replaceMethod函數,該函數的行為可以分為兩種:如果類中不存在name指定的方法,則類似于class_addMethod函數一樣會添加方法;如果類中已存在name指定的方法,則類似于method_setImplementation一樣替代原方法的實現。

class_getMethodImplementation函數,該函數在向類實例發送消息時會被調用,并返回一個指向方法實現函數的指針。這個函數會比method_getImplementation(class_getInstanceMethod(cls,name))更快。返回的函數指針可能是一個指向runtime內部的函數,而不一定是方法的實際實現。例如,如果類實例無法響應selector,則返回的函數指針將是運行時消息轉發機制的一部分。

class_respondsToSelector函數,我們通常使用NSObject類的respondsToSelector:或instancesRespondToSelector:方法來達到相同目的。

緩存(cache)

用于緩存最近使用的方法。一個接收者對象接收到一個消息時,它會根據isa指針去查找能夠響應這個消息的對象。在實際使用中,這個對象只有一部分方法是常用的,很多方法其實很少用或者根本用不上。這種情況下,如果每次消息來時,我們都是methodLists中遍歷一遍,性能勢必很差。這時,cache就派上用場了。在我們每次調用過一個方法后,這個方法就會被緩存到cache列表中,下次調用的時候runtime就會優先去cache中查找,如果cache沒有,才去methodLists中查找方法。這樣,對于那些經常用到的方法的調用,提高了調用的效率。

協議(objc_protocol_list)

協議相關的操作包含以下函數:

//添加協議

BOOL class_addProtocol(Class cls,Protocol *protocol);

//返回類是否實現指定的協議

BOOL class_conformsToProtocol(Class cls,Protocol *protocol);

//返回類實現的協議列表

Protocol * class_copyProtocolList(Class cls,unsigned int *outCount);

class_conformsToProtocol函數可以使用NSObject類的conformsToProtocol:方法來替代。

class_copyProtocolList函數返回的是一個數組,在使用后我們需要使用free()手動釋放。

objc_class結構體的應用

沒有實際應用的知識講解都是耍流氓

@property的本質

這里有一個孫源的面試題是:@property的本質是什么?ivar、getter、setter是如何生成并添加到這個類中的。

簡單點說就是:@property = ivar + getter + setter;

也就是生成實例變量及對應的存取方法。

那這跟我們這里所講的objc_class結構體有什么關系呢?

因為@property對應的ivar、getter和setter都會對應添加到我們結構體中的ivar_list、method_list中。也就是說我們每次增加一個屬性,系統都會在ivar_list添加一個成員變量的描述,在method_list中增加setter與getter方法的描述。

其他Runtime結構體

objc_object結構體

除了類有對應的結構體,對象也有對應的結構體。

typedef struct objc_object *id;

id就是指向對象對應的結構體。對象的結構體只有isa指針,指向它所屬的類。而類的結構體也有isa指針指向它的元類。

所以在OC中objc_class結構體是繼承自objc_object:

struct objc_object {

Class isa? OBJC_ISA_AVAILABILITY;

};

struct objc_class : objc_object {

Class superclass;

cache_t cache;// formerly cache pointer and vtable

class_data_bits_t bits;// class_rw_t * plus custom rr/alloc flags

class_rw_t *data(){

return bits.data();

}

};

Category結構體

Category的定義如下:

typedef struct objc_category *Category;

Category是一個objc_category結構體的指針,objc_category的定義如下:

struct objc_category {

char *category_name ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;

char *class_name ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;

struct objc_method_list *instance_methods ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

struct objc_method_list *class_methods ? ? ? ? ? ? ? ? ? ?OBJC2_UNAVAILABLE;

struct objc_protocol_list *protocols ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

} OBJC2_UNAVAILABLE;

通過上面的結構體,大家可以很清楚的看出存儲的內容。我們繼續往下看,打開objc源代碼,在objc-runtime-new.h中我們可以發現如下定義:

struct category_t {

const char *name;

classref_t cls;

struct method_list_t *instanceMethods;

struct method_list_t *classMethods;

struct protocol_list_t *protocols;

struct property_list_t *instanceProperties;

};

上面的定義需要提到的地方有三點:

name是指class_name而不是category_name

cls是要擴展的類對象,編譯期間是不會定義的,而是在Runtime階段通過name對應到對應的類對象

instanceProperties表示Category里所有的properties,這就是我們可以通過objc_setAssociatedObject和objc_getAssociatedObject增加實例變量的原因,不過這個和一般的實例變量是不一樣的

參考

Objective-C Runtime運行時之一:類與對象

謝謝!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,746評論 0 9
  • 我們常常會聽說 Objective-C 是一門動態語言,那么這個「動態」表現在哪呢?我想最主要的表現就是 Obje...
    Ethan_Struggle閱讀 2,222評論 0 7
  • Objective-C語言是一門動態語言,它將很多靜態語言在編譯和鏈接時期做的事放到了運行時來處理。這種動態語言的...
    有一種再見叫青春閱讀 600評論 0 3
  • 原文出處:南峰子的技術博客 Objective-C語言是一門動態語言,它將很多靜態語言在編譯和鏈接時期做的事放到了...
    _燴面_閱讀 1,244評論 1 5
  • 不要等到明天,明天太遙遠,今天就行動。 須讀:看完該文章你能做什么? 字符串的轉換(大小寫,C轉OC,OC轉C) ...
    liyuhong閱讀 474評論 0 0