Category底层原理实现
本文首發(fā)于公眾號(hào)【程序員華仔】
今天一起來(lái)探究下Category的底層實(shí)現(xiàn)原理。
1.Category 簡(jiǎn)介
Category是Object-C 2.0之后新增加的特性功能,主要用于對(duì)已存在類增加方法、協(xié)議和屬性。在實(shí)際項(xiàng)目中,我們主要用它來(lái)擴(kuò)展類的功能,實(shí)現(xiàn)類的多繼承等事情。
2. Category 源碼初探
要對(duì)Category的源碼進(jìn)行研究,首先我們建個(gè)實(shí)際的類,通過實(shí)際類的分析來(lái)看Category的內(nèi)部情況。
這里先新建個(gè)Person類,
實(shí)現(xiàn)Person+PersonTest的類目。
@interface Person (PersonTest) {
}
@property(nonatomic,strong)NSString *name;
+ (void)loadAllData;
- (void)saveAllData;
@end
@implementation Person (PersonTest)
+ (void)loadAllData {
}
- (void)saveAllData {? ?
}
@end
以上Person類,新增加了類方法,對(duì)象方法和屬性。我們來(lái)看看轉(zhuǎn)換成C++代碼之后,內(nèi)部什么情況?
使用clang命令把上面的OC代碼轉(zhuǎn)換成C++代碼。具體為:進(jìn)入Person+PersonTest.m 文件下,在終端執(zhí)行一下命令:
clang -rewrite-objc Person+PersonTest.m -o tPerson.cpp
看過我之前寫過的文章的同學(xué)就知道,其實(shí)還有一個(gè)命令,可以把Object-C轉(zhuǎn)成C++代碼。如下命令:
xcrun -sdk iphoneos clang -archarm64 -rewrite-objc Person+PersonTest.m -o tPerson.cpp
其實(shí)兩個(gè)命令,后面部分一模一樣,只是后一個(gè)命令,使用了xcrun。
通過查看cpp文件,我們可以知道以下情況:
1.類方法和對(duì)象方法,都對(duì)應(yīng)_method_list_t的結(jié)構(gòu)體。
2.屬性,對(duì)應(yīng)為_prop_list_t的結(jié)構(gòu)體。
3.協(xié)議,對(duì)應(yīng)為_protocol_list_t的結(jié)構(gòu)體(cpp文件中未體現(xiàn),查資料而得)。
4.有一個(gè)_category_t結(jié)構(gòu),里面有對(duì)應(yīng)類方法,對(duì)象方法,協(xié)議和屬性的成員變量,存儲(chǔ)著Person類的Category信息。
具體如下的信息:
1.static struct /*_method_list_t*/ { //對(duì)象方法
unsigned int entsize;? // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_PersonTest __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"saveAllData", "v16@0:8", (void *)_I_Person_PersonTest_saveAllData}}
};
2.static struct /*_method_list_t*/ { //類方法
unsigned int entsize;? // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_PersonTest __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"loadAllData", "v16@0:8", (void *)_C_Person_PersonTest_loadAllData}}
};
3.static struct /*_prop_list_t*/ {
unsigned int entsize;? // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person_$_PersonTest __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"name","T@\"NSString\",&,N"}}
};
4.struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
3. Category 底層源碼
了解了_category_t結(jié)構(gòu)體之后,我們看下Category底層源碼。
Category底層源碼是由蘋果公司開發(fā)并已經(jīng)對(duì)外開源了,我們可以在蘋果開源網(wǎng)站上獲取,具體代碼在objc4庫(kù)上。
具體下載objc4庫(kù)的方式,如下圖(這里我獲取最新的庫(kù)版本為objc4-841.13)
下載好objc4庫(kù)之后,_category_t結(jié)構(gòu)體代碼主要在objc-runtime-new(.h.m)文件厘米。如下圖
通過這段代碼,告訴我們了category的結(jié)構(gòu)信息如下:
1.name 為名稱
2.cls 為類
3. instanceMethods為對(duì)象方法,且是一個(gè)列表(包含該category所有新增加的方法)。
4. classMethods為類方法,同instanceMethods一樣也是列表。
5.protocols為協(xié)議列表
6. instanceProperties為實(shí)例屬性列表
7. _classProperties為類屬性列表。
由此我們可知在category中可以添加對(duì)象方法、類方法、協(xié)議實(shí)現(xiàn)、屬性。
這里有個(gè)面試題:
問:Category為什么只能加方法不能加屬性?
答:分類的實(shí)現(xiàn)原理是將category中的類方法、對(duì)象方法、屬性、協(xié)議數(shù)據(jù)放在category_t結(jié)構(gòu)體中,然后將結(jié)構(gòu)體內(nèi)的方法列表拷貝到類對(duì)象的方法列表。
Category可以添加屬性,但是并不會(huì)自動(dòng)生成成員變量及set/get方法。因?yàn)閏ategory_t結(jié)構(gòu)體中并不存在成員變量。通過之前對(duì)對(duì)象的分析我們知道成員變量是存放在實(shí)例對(duì)象中的,并且編譯的那一刻就已經(jīng)決定好了。而分類是在運(yùn)行時(shí)才去加載的。那么我們就無(wú)法再程序運(yùn)行時(shí)將分類的成員變量中添加到實(shí)例對(duì)象的結(jié)構(gòu)體中。因此分類中不可以添加成員變量。
了解了Category的底層實(shí)現(xiàn),我們來(lái)了解下Category的加載過程。
4.category的加載
我們都知道Objective-C的運(yùn)行依賴于runtime,在OC運(yùn)行時(shí),runtime的入口如下。
在這段入口代碼中,_dyld_objc_notify_register函數(shù)才是OC初始化的開始。
category加載到類中是在map_images的時(shí)候發(fā)生的,而map_images最終會(huì)調(diào)用到objc-runtime-new.mm中的_read_images方法。
在_read_images函數(shù)中,主要做以下事情:
1.通過_getObjc2CategoryList函數(shù)獲取到category的列表。
2.將category中的對(duì)象方法,協(xié)議以及屬性添加到類中。
3.將category中的類方法、協(xié)議以及類屬性添加到類的元類中。
然后會(huì)調(diào)用remethodizeClass方法,再調(diào)用attachCategories。在attachCategories函數(shù)中,將category中的對(duì)象方法、屬性、協(xié)議添加到了類中。最后會(huì)將這些信息添加到類的data中,這樣就實(shí)現(xiàn)了Category類與類中的元類的聯(lián)系。
5.Categroy方法先后調(diào)用問題。
在實(shí)際開發(fā)過程中,如果在category定義了和類中一樣的方法,這時(shí)候在方法調(diào)用的時(shí)候,調(diào)用的是category中的方法還是類中的方法呢?
通過閱讀attachLists函數(shù),我們知道了:在category中的方法加載到類的時(shí)候,其方法會(huì)優(yōu)先添加到類方法列表的前面,并未替換原有的方法。而方法的調(diào)用是順序查找的,所以會(huì)先調(diào)用category中的方法。
總結(jié)
以上是生活随笔為你收集整理的Category底层原理实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从此甩掉光驱 U盘安装系统最详攻略(转自
- 下一篇: pagefile.sys删除