前言
上一篇iOS底层原理06:类的底层原理探索(1)已经对类的底层原理有了初步的探索,这篇文章将继续探索。
前期准备
定义两个类
- 类
Person继承自NSObject
//import "Person.h
@interface Person : NSObject{
NSString *hobby;
}
@property (nonatomic,copy) NSString *name;
- (void)eat;
+ (void)run;
@end
//import "Person.m
@implementation Person
- (void)eat
{}
+ (void)run
{}
@end
- 类
Teacher继承自Person
@interface Teacher : Teacher
@end
@implementation Teacher
@end
- 在main中创建两个对象:
p1和t1
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *p1 = [Person alloc];
Teacher *t1 = [Teacher alloc];
MyNSLog(@"%@ --%@ ", p1,t1);
}
return 0;
}
探索 属性列表
上一篇文章iOS底层原理06:类的底层原理探索(1),我们探索到了class_rw_t,并且在源码中发现,结构体中有提供相应的方法去获取 属性列表、方法列表等,如下所示
我们现在将lldb调试到上一篇内容的位置
lldb执行
p $3.properties()
(lldb) p $3.properties()
(const property_array_t) $4 = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000100003240
arrayAndFlag = 4294980160
}
}
}
通过list的地址获取信息,执行p *$4.list
(lldb) p *$4.list
(property_list_t) $5 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 1
first = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
}
}
执行p $5.get(0)获取第一个属性
(lldb) p $5.get(0)
(property_t) $6 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
执行p $5.get(1)获取第二个属性,发现lldb报数组越界的错误信息。
说明明 property_list 中只有 一个属性name。
探索的过程如下图
struct property_t {
const char *name;
const char *attributes;
};
小结
类的属性存储在bits中,通过通过bits --> data() --> properties() --> list获取。
探索 方法列表
我们仿照属性列表的探索,在lldb下调试
-
通过执行
p $3.methods()获得具体的方法列表的内部结构,methods()是class_rw_t提供的方法。 -
由
count = 4可知,方法列表中存储了4个方法。因此获取第5个方法是,报数组越界的错误。
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; }
};
};
小结
类的实例方法存储在类的bits中,通过bits --> methods() --> list获取实例方法列表,类中的方法列表存储了实例方法,属性的set方法 和 get方法
探索 类方法存储
我们在方法列表的探索中发现,前面定义的类方法run()并没有存在在方法列表中,那么类方法存储在哪呢?
在iOS底层原理05:isa底层原理探索下文章中,我们得知,类对象(class) isa 指向 元类(Meta class),元类存储的类的信息,那么在元类的bits中是否存储了类方法呢?我们继续通过lldb调试。
小结
类的类方法存储在元类的bits中,通过元类bits --> data() --> methods() --> list获取类方法列表。
探索 成员变量
通过上面的探索,我们知道了属性,方法和类方法的存储,那么成员变量又存储在哪呢?
在的源码中,发现还有个ro()方法,那么我们猜测,成员变量会不会存在这里呢?
通过lldb调试
获取第一个数据
(lldb) p $7.get(0)
(ivar_t) $9 = {
offset = 0x0000000100003290
name = 0x0000000100001e91 "hobby"
type = 0x0000000100001f79 "@\"NSString\""
alignment_raw = 3
size = 8
}
struct ivar_t {
#if __x86_64__
// *offset was originally 64-bit on some x86_64 platforms.
// We read and write only 32 bits of it.
// Some metadata provides all 64 bits. This is harmless for unsigned
// little-endian values.
// Some code uses all 64 bits. class_addIvar() over-allocates the
// offset for their benefit.
#endif
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
小结
类的成员变量以结构体ivar_t的形式存储在bits中,通过通过bits --> data() -->ro() --> ivars获取。