該文為分類原理的簡單記錄逃糟,總結(jié)自如下文章绽族,感謝作者分享:
1卵酪、分類的結(jié)構(gòu)
分類的結(jié)構(gòu)體如下
(源碼詳見:objc-runtime-new.h )
struct category_t {
const char *name; //類名
classref_t cls; //分類所屬的類
//category中所有給類添加的實(shí)例方法的列表(instanceMethods)
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
//category中所有添加的類方法的列表(classMethods)
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
//category實(shí)現(xiàn)的所有協(xié)議的列表(protocols)
struct protocol_list_t *protocols;
//category中添加的所有屬性(instanceProperties)
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
2烹骨、分類的編譯過程
通過如下命令將分類的m文件進(jìn)行轉(zhuǎn)換莫绣,分析其編譯過程
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx+xxx.m
structcategory_t{
constchar *name;// 類名
classref_tcls; // 分類所屬的類
// 實(shí)例方法列表
structmethod_list_t*instanceMethods;
// 類方法列表
structmethod_list_t*classMethods;
// 遵循的協(xié)議列表
structprotocol_list_t*protocols;
// 屬性列表
structproperty_list_t*instanceProperties;
// 如果是元類,就返回類方法列表诗鸭;否則返回實(shí)例方法列表
method_list_t*methodsForMeta(boolisMeta) {
if(isMeta) {
return classMethods;
}else{
return instanceMethods;
}
}
// 如果是元類染簇,就返回 nil,因?yàn)樵悰]有屬性强岸;否則返回實(shí)例屬性列表锻弓,關(guān)于分類的實(shí)例屬性,下文闡述
property_list_t*propertiesForMeta(boolisMeta) {
if(isMeta) {
return nil;// 元類沒有屬性;
}else{
return instanceProperties;//實(shí)例屬性
}
}
};
3蝌箍、分類的實(shí)現(xiàn)原理:
由上可得青灼,分類在編譯過程中,會生成 類方法列表
妓盲、實(shí)例方法列表
杂拨、屬性列表
等,但是卻 沒有 實(shí)例變量列表(_ivar_list_t)
悯衬,可對比分類所屬類的編譯結(jié)果看弹沽,分類所屬類是存在實(shí)例變量列表
的。然后,再來對比 實(shí)例方法列表
贷币,還能發(fā)現(xiàn)分類的 實(shí)例方法列表
中,并未對分類屬性生成 getter/setter
方法亏狰。
所以役纹,這就是為什么 分類不能添加屬性
的原因。
4暇唾、分類的加載
- 分類是在 運(yùn)行時(shí) 進(jìn)行加載的促脉,OC運(yùn)行時(shí),入口方法為
_objc_init
其加載調(diào)用棧如下:
_objc_init //runtime的初始化函數(shù)策州,進(jìn)行初始化操作瘸味,注冊了鏡像狀態(tài)改變時(shí)的回調(diào)函數(shù)
└── map_2_images //加鎖并調(diào)用 map_images_nolock
└── map_images_nolock //完成所有 class 的注冊、fixup等工作够挂,還有初始化自動釋放池旁仿、初始化 side table 等工作并在函數(shù)后端調(diào)用了 _read_images
└── _read_images //加載類、Protocol孽糖、Category枯冈,加載分類的代碼就寫在 _read_images 函數(shù)的尾部
_objc_init 函數(shù)在 objc-os.mm 中,_read_images 方法在objc-runtime-new.mm 中办悟。
- 加載過程:
1尘奏、把分類的 實(shí)例方法
、屬性
病蛉、協(xié)議
添加到類的實(shí)例對象中原本存儲的 實(shí)例方法
炫加、屬性
、協(xié)議列表
的 前面 铺然;
2俗孝、把分類的 類方法
和協(xié)議
添加到類的元類
上。
如此魄健,保證了分類方法 優(yōu)先調(diào)用驹针,注意,不是覆蓋诀艰,而是共同存在在實(shí)例方法列表中柬甥,只是分類在前而已。