一、类的属性存储
这部我们将分析类的属性存储,那么就先添加两个属性
@interface LGPerson : NSObject {
NSString *hobby;
}
@property (nonatomic, strong) NSString *name;
@end
定义好之后在运行是,打印一下类的内存结构


isa 和 superclass 所占空间都知道了,现在不确定的 cache_t,现将这个结构体先贴出来

typedef uint32_t mask_t;
所以 bits 所在位置就是 isa(8字节) + superclass(8字节) + cache_t(16字节)= 32字节,所以如果要得到 bits 首地址,就要偏移32个字节,因为地址都是16进制的,需要需要32 转为16进制就是 0x20,上面有打印有显示指向 isa 的首地址为 0x1000014b8 ,加上 0x20 的话就是 0x1000014d8 .这里不知道怎么来的就用计算器16进制进行相加一下。然后我们开始读取 bits 内的数据。
因为 bit 为 class_data_bits_t 类型,读取的时候强转一下,再有我们是要从地址里读取到bits的信息所以,强转的时候就需要带一个 * ,最后就是 (class_data_bits_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;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
查看源码代码发现方法,协议,属性都存在这里面,那就将刚才的 (class_rw_t *) 类型的数据打印出来,看看目前为止到底存了些什么东西












总结:类的属性并没在我们以为的 method 里,而是在 ro 里,至于为什么,先一步步往后看。
二、类的方法存储

第一部分的内容里有分析到类的属性存储在 baseProperties 里,再仔细一看然后在 ro 里有看到类似属性列表的字段 baseMethodList ,于是就把 baseMethodList 读出来,看是否是方法存储的地方

struct method_t {
SEL name;
const char *types;
MethodListIMP imp;
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ return lhs.name < rhs.name; }
};
};
再回过来看刚才打印的方法列表里面的内容,里面有个字段 count = 3,方法列表是一个列表,说明count在这里代表的是方法的个数为3个呢?


打印到这里方法列表都是系统自动添加的方法,那如果是我们自己写的方法,会在这里面吗?验证一下


1)在类和元类的获取实例化方法里查找方法的代码
void findInstanceMethod_classtoMetaClass(Class pClass) {
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(goodMorning));
Method method2 = class_getInstanceMethod(metaClass, @selector(goodMorning));
Method method3 = class_getInstanceMethod(pClass, @selector(goodAfternoon));
Method method4 = class_getInstanceMethod(metaClass, @selector(goodAfternoon));
NSLog(@"%p -- %p -- %p -- %p", method1, method2, method3, method4);
NSLog(@"%s\n", __func__);
}
2)在类和元类的获取类方法里查找方法的代码
void findClassMethod_classToMetaClass(Class pClass) {
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(goodMorning));
Method method2 = class_getClassMethod(metaClass, @selector(goodMorning));
Method method3 = class_getClassMethod(pClass, @selector(goodAfternoon));
Method method4 = class_getClassMethod(metaClass, @selector(goodAfternoon));
NSLog(@"%p -- %p -- %p -- %p", method1, method2, method3, method4);
NSLog(@"%s\n", __func__);
}
然后在main函数里调用并查看结果

第一个打印结果:在类的获取实例化方法里面找到了实例化方法,在元类的获取实例化方法里面找到类方法。也就是实例化方法是类的实例化方法,类方法是元类的实例化方法
第二个打印结果:在类里和元类的获取类方法里都找到类方法,这里在获取类方法里面就找不到实例化方法了。但是结果就有点感觉怪怪的了,为什么在元类的元类里还能找到类方法呢。我们先看看这个获取类方法的接口实现源码


所以上面的第二个打印结果其实并不是真的在元类的元类也就是根元类里找到的,而是在元类里找到的。返回的还是在元类里的实例方法地址。
接下来我们就通过打印来验证一下,在元类找类方法

总结: 1)成员变量存储在ivar; 2)属性存储在property 和 ivar; 3)对象方法存储在类里面; 3)类方法存储在元类里面。
拓展: 1)获取类里面属性和成员变量列表
void testObjc_copyIvar_copyProperies(Class pClass) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i = 0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名称
const char *cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
NSLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i = 0; i < pCount; ++i) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//获取属性值
NSLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
验证:

2)获取方法列表
void findObjc_copyMethodList(Class pClass) {
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i = 0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *methodName = [NSString stringWithUTF8String:method_getName(method)];
NSLog(@"findObjc_copyMethList:%@", methodName);
}
}
验证:
