在谈及面向对象编程的时候,总是离不开 对象
与 类
。对象
是对客观事物的抽象,类
是对 对象
的抽象。它们的关系是,对象
是 类
的实例,类
是 对象
的模板。
在OC中对象
的信息存储在类
中,那么类
是以什么样的结果存在的呢,我们可以在objc
的源码中找到下面代码,从中可以发现objc_class
是继承objc_object
的,也就是说 类
也是一个对象。这也是OC中万物皆对象
的由来。因其继承自 objc_objec
t ,自然默认就含有了 objc_object
的成员 isa
。
isa和继承

我们可以通过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
信息

获取属性列表
在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
获取方法列表

-
通过
p $4.methods()
获得具体的方法列表的list结构,其中methods
也是class_rw_t
提供的方法 -
通过打印的count = 4可知,存储了
4
个方法,可以通过p $7.get(i)
内存偏移
的方式获取单个方法,i 的范围是0-3 -
如果在打印
p $7.get(4)
,获取第五个方法,也会报错,提示数组越界
网友评论