寫(xiě)一個(gè) Person
的分類:Person+DO
Person+DO.h
文件:
#import "Person.h"
@interface Person (DO) <NSCopying>
@property (nonatomic, assign) int number;
- (void)testInstanceMethod;
+ (void)testClassMethod;
@end
Person_DO.m
文件:
#import "Person+DO.h"
@implementation Person (DO)
- (void)testInstanceMethod{}
+ (void)testClassMethod{}
@end
在終端輸入以下命令:
$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+DO.m -o Person+DO-arm64.cpp
打開(kāi) Person+DO-arm64.cpp
該文件贩毕,我們想要的都在這里面。
struct _category_t {
const char *name; // 類名
struct _class_t *cls;
const struct _method_list_t *instance_methods; // 對(duì)象方法列表
const struct _method_list_t *class_methods; // 類方法列表
const struct _protocol_list_t *protocols; // 協(xié)議列表
const struct _prop_list_t *properties; // 屬性列表
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_DO __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"testInstanceMethod", "v16@0:8", (void *)_I_Person_DO_testInstanceMethod}}
};
_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_DO
:從名字(INSTANCE_METHODS)可看出是對(duì)象方法列表結(jié)構(gòu)體
testInstanceMethod
:正是我們分類中定義的對(duì)象方法
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_$_DO __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"testClassMethod", "v16@0:8", (void *)_C_Person_DO_testClassMethod}}
};
_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_DO
:從名字(CLASS_METHODS)可看出是類方法列表結(jié)構(gòu)體
testClassMethod
:正是我們分類中定義的類方法
static const char *_OBJC_PROTOCOL_METHOD_TYPES_NSCopying [] __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"@24@0:8^{_NSZone=}16"
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", 0}}
};
struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = {
0,
"NSCopying",
0,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying,
0,
0,
0,
0,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_NSCopying = &_OBJC_PROTOCOL_NSCopying;
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_Person_$_DO __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_NSCopying
};
可看出,協(xié)議方法先通過(guò) _method_list_t
結(jié)構(gòu)體存儲(chǔ)凸克,之后通過(guò) _protocol_t
結(jié)構(gòu)體存儲(chǔ)在 _OBJC_CATEGORY_PROTOCOLS_$_Person_$_DO
中和 _protocol_list_t
結(jié)構(gòu)體一一對(duì)應(yīng)肾筐。
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_$_DO __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"number","Ti,N"}}
};
從屬性列表結(jié)構(gòu)體中慷吊,發(fā)現(xiàn)了我們自己寫(xiě)的 number
屬性
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_Person;
static struct _category_t _OBJC_$_CATEGORY_Person_$_DO __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_DO,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_DO,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_DO,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_DO,
};
static void OBJC_CATEGORY_SETUP_$_Person_$_DO(void ) {
_OBJC_$_CATEGORY_Person_$_DO.cls = &OBJC_CLASS_$_Person;
}
struct _class_t OBJC_CLASS_$_Person
:_class_t
類型的 OBJC_CLASS_$_Person
結(jié)構(gòu)體
_OBJC_$_CATEGORY_Person_$_DO.cls = &OBJC_CLASS_$_Person;
:cls
指針指向分類的主類類對(duì)象 Person
的地址
至此馍惹,通過(guò)以上分類的源碼瑞眼,我們?cè)诜诸愔卸x的 對(duì)象方法
龙宏、 類方法
、 屬性
伤疙、協(xié)議
等都存放在 catagory_t
結(jié)構(gòu)體中银酗。
總結(jié):
- Category的實(shí)現(xiàn)原理:將分類中的方法,屬性徒像,協(xié)議數(shù)據(jù)放在
category_t
結(jié)構(gòu)體中黍特,然后將結(jié)構(gòu)體內(nèi)的方法列表拷貝到類對(duì)象的方法列表中。 - Category中不能加屬性:Category可以添加屬性锯蛀,但是并不會(huì)自動(dòng)生成成員變量及
set/get
方法灭衷。因?yàn)?category_t
結(jié)構(gòu)體中并不存在成員變量。
進(jìn)一步分析:
成員變量是存放在實(shí)例對(duì)象中的旁涤,并且編譯使就已經(jīng)決定好了翔曲。而分類是在運(yùn)行時(shí)才去加載的,無(wú)法在程序運(yùn)行時(shí)將分類的成員變量添加到實(shí)例對(duì)象的結(jié)構(gòu)體中劈愚。因此分類中不可以添加成員變量瞳遍。
load 和 initialize
objc-loadmethod.mm
文件中:
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
優(yōu)先調(diào)用類的 load
方法,之后調(diào)用分類的 load
方法
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
initialize
特點(diǎn):
- 第一次使用類的時(shí)候會(huì)調(diào)用(第一次接收到消息)
- 調(diào)用子類的
initialize
之前菌羽,會(huì)先保證調(diào)用父類的initialize
方法 - 如果之前已經(jīng)調(diào)用過(guò)
initialize
掠械,就不會(huì)再調(diào)用initialize
方法 - 當(dāng)分類重寫(xiě)
initialize
方法時(shí)會(huì)先調(diào)用分類的方法
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
load
方法通過(guò)直接拿到 load
方法地址進(jìn)行調(diào)用
總結(jié):
load
和 initialize
區(qū)別:
-
load
是根據(jù)函數(shù)地址直接調(diào)用
initialize
是通過(guò)objc_msgSend
調(diào)用 -
load
是runtime
加載類、分類的時(shí)候調(diào)用(只會(huì)調(diào)用1次)
initialize
是類第一次接收到消息的時(shí)候調(diào)用算凿,每一個(gè)類只會(huì)initialize
一次(父類的initialize
方法可能會(huì)被調(diào)用多次份蝴,子類未實(shí)現(xiàn)) - 分類中
load
方法不會(huì)覆蓋本類的load
方法
如果分類實(shí)現(xiàn)了initialize
方法犁功,就覆蓋類本身的initialize
方法