在谈及面向对象编程的时候,总是离不开 对象 与 类 。对象 是对客观事物的抽象,类 是对 对象 的抽象。它们的关系是,对象 是 类 的实例,类 是 对象 的模板。
在OC中对象的信息存储在类中,那么类是以什么样的结果存在的呢,我们可以在objc的源码中找到下面代码,从中可以发现objc_class 是继承objc_object 的,也就是说 类 也是一个对象。这也是OC中万物皆对象的由来。因其继承自 objc_object ,自然默认就含有了 objc_object 的成员 isa。
isa和继承
image.png
我们可以通过isa流程和继承关系图发现
isa的走向有以下几点说明:
-
实例对象(Instance of Subclass)的 isa指向类(class) -
类对象(class) isa指向元类(Meta class) -
元类(Meta class)的isa指向根元类(Root metal class) -
根元类(Root metal class) 的isa指向它自己本身,形成闭环,这里的根元类就是NSObject
superclass(即继承关系)的走向也有以下几点说明:
-
类之间 的继承关系:-
子类(subClass)继承自父类(superClass) -
父类(superClass)继承自根类(RootClass),此时的根类是指NSObject -
根类继承自nil,所以根类即NSObject可以理解为万物起源,即无中生有
-
-
元类也存在继承,元类之间的继承关系如下:-
子类的元类(metal SubClass)继承自父类的元类(metal SuperClass) -
父类的元类(metal SuperClass)继承自根元类(Root metal Class) -
根元类(Root metal Class)继承于根类(Root class),此时的根类是指NSObject
-
-【注意】实例对象之间没有继承关系,类之间有继承关系
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
// Class ISA;//8字节
Class superclass; //8字节
cache_t cache; //8字节 // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
-
isa:关于isa的在[OC底层04:isa和类的关联],(https://www.jianshu.com/p/ad042414e5e4)可以找到答案,继承自objc_object的isa,占8字节 -
superclass:指向父类,依旧是Class类型,所以也占8字节 -
cache: 方法缓存列表,存储了最近调用的方法 -
bits:class_data_bits_t类型,存储详细的类信息
objc_class、objc_object、isa、object、NSObject
cache_t
我们进入cache_t,发现主要有如下几个属性
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic<struct bucket_t *> _buckets; // 是一个结构体指针类型,占8字节
explicit_atomic<mask_t> _mask; //是mask_t 类型,而 mask_t 是 unsigned int 的别名,占4字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic<uintptr_t> _maskAndBuckets; //是指针,占8字节
mask_t _mask_unused; //是mask_t 类型,而 mask_t 是 uint32_t 类型定义的别名,占4字节
#if __LP64__
uint16_t _flags; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
#endif
uint16_t _occupied; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
通过计算cache_t中属性大小为16字节
所以我们可以通过获取类的首地址,通过平移32字节来获取bits中的内容
获取bits
准备工作
定义一个继承自NSObject的类YPPerson
@interface YPPerson : NSObject
{
NSString *hobby;
}
@property (nonatomic, copy) NSString *yp_name;
- (void)sayHello;
+ (void)sayBye;
@end
@implementation YPPerson
- (void)sayHello{
}
+ (void)sayBye{
}
@end
通过lldb调试打印bits信息
image.png
获取属性列表
在class_data_bits_t方法class_rw_t,点进去class_rw_t中提供获取了属性列表、方法列表等方法
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->methods;
} else {
return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->properties;
} else {
return property_array_t{v.get<const class_ro_t *>()->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>()->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
}
}
所以我们在刚刚获取到的bits信息基础上,通过class_rw_t提供的方法,我们可以继续打印出属性列表
获取属性列表
-
p $3.properties()命令中的propertoes方法是由class_rw_t提供的,方法中返回的实际类型为property_array_t(数组) -
由于
list的类型是property_list_t,是一个指针,所以通过p *$5获取内存中的信息,同时也证明bits中存储了property_list,即属性列表 -
p $6.get(1),获取YPPerson中的成员变量, 发现会报错,提示数组越界了,说明property_list中只有一个属性yp_name
获取方法列表
image.png
-
通过
p $4.methods()获得具体的方法列表的list结构,其中methods也是class_rw_t提供的方法 -
通过打印的count = 4可知,存储了
4个方法,可以通过p $7.get(i)内存偏移的方式获取单个方法,i 的范围是0-3 -
如果在打印
p $7.get(4),获取第五个方法,也会报错,提示数组越界













网友评论