方法的存储以及方法缓存的存储
类和结构体
首先定义一个类testClass,用clang重写一下,为了方便,不要加其他系统头文件,不然还得指定路径
类被编译成了结构体
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
。。。。
}
typedef struct objc_class *Class;
struct NSObject_IMPL {
Class isa;
};
struct testClass_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int a;
NSString *b;
};
类中的成员对象变成结构体的成员,方法是否声明不影响c++文件,下面还有属性的set和get方法
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[6];
} _OBJC_$_INSTANCE_METHODS_testClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
6,
{{(struct objc_selector *)"sayABC", "v16@0:8", (void *)_I_testClass_sayABC},
{(struct objc_selector *)"eatFood", "v16@0:8", (void *)_I_testClass_eatFood},
{(struct objc_selector *)"c", "i16@0:8", (void *)_I_testClass_c},
{(struct objc_selector *)"setC:", "v20@0:8i16", (void *)_I_testClass_setC_},
/*
static void _I_testClass_setC_(testClass * self, SEL _cmd, int c) { (*(int *)((char *)self
+OBJC_IVAR_$_testClass$_c)) = c; }
这个“v20@0:8i16”对应上面的_I_testClass_setC_函数 -- v代表void,返回类型。20指总共
长度。@代表id类型,起始位置为0。:代表sel,起始位置为8。i代表int,起始位置为16
*/
{(struct objc_selector *)"d", "@16@0:8", (void *)_I_testClass_d},
{(struct objc_selector *)"setD:", "v24@0:8@16", (void *)_I_testClass_setD_}}
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_testClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"startRun", "v16@0:8", (void *)_C_testClass_startRun}}
};
注意,类方法和实例方法没有在一起,类方法在下面那个结构体里面
属性和方法的存储
根据上面类的结构,使用lldb在xcode中打印
(由于失误把之前的数据清理,将就一下)$$3就是上面的 $6,在method里面找方法
可以看到,第一个还是正常的方法,后面几个就不知道是什么东西了,可能是缓存之类的东西
同样,属性也是一样,第一个貌似是正常的
class_rw_t结构
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
在class_rw_t里,发现有个class_ro_t,里面有方法和属性list,再次使用lldb查看
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
先看method_list_t,可以发现是继承entsize_list_tt,这个结构体里面有两方法都可以用,可以拿到指定的方法,但是把所有的方法打印出来以后,并没有发现存在startRun
Element& getOrEnd(uint32_t i) const {
assert(i <= count);
return *(Element *)((uint8_t *)&first + i*entsize());
}
Element& get(uint32_t i) const {
assert(i < count);
return getOrEnd(i);
}
然后看ivar,有四个,两个属性加两个成员变量
class_ro_t这个结构体里面还有属性和协议列表,就不一一打印了。
startRun这个类方法找不到,可能在原类里面,懒得算,直接用object_getClass来拿到isa使用lldb再次打印,第一个就是startRun
最后总结
对象的方法和属性都在类结构体的class_rw_t结构体的class_ro_t中,多个对象公用一个类结构体,注意这里类中存的属性并不是值,而是属性的名字和类型,值还是在对象里面,而方法是公用的,所以就直接存在类中,类方法在原类中。
既然类在编译的时候已经确定(使用clang可以看到cpp文件),那么使用分类添加的方法和使用runtime添加的属性在哪里呢?
给TPerson加个分类的gogogo方法发现他会在ro中