对象的分类
实例对象 Instance
类对象 Class
元类对象 meta-class
三者之间的关系
首先说说三者的结构
- 实例对象 Instance 上篇说过,通过clang 编译可得出结论结构体中「只」会存储
- isa
- 成员变量
- 类对象 Class 也就是类 查看 objc_runtime_new.h 可见
- isa
- superclass
- cache_t 方法缓存
- class_data_bits_t 具体的类信息
- 元类对象 Class 和类对对象的结构体相同但是元类对象中只
- isa
- superclass
- class_data_bits_t (方法列表只存类方法 classmethod)
下面来看下 class_data_bits_t 中的源码
// objc_class继承于objc_object,因此
// objc_class中也有isa结构体
struct objc_class : objc_object {
// ISA占8位
// Class ISA;
// superclass占8位
Class superclass;
// 缓存的是指针和vtable,目的是加速方法的调用 cache占16位
cache_t cache; // formerly cache pointer and vtable
// class_data_bits_t 相当于是class_rw_t 指针加上rr/alloc标志
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
查看class_data_bits_t源码 会发现 有两个重要的结构体 class_ro_t ,class_rw_t
- 先来看class_ro_t:
// class_ro_t结构体存储了类在编译期就已经确定的属性、方法以及遵循的协议
// 因为在编译期就已经确定了,所以是ro(readonly)的,不可修改
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;
}
};
- 再来看 class_rw_t
// 类的方法、属性、协议等信息都保存在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_ro_t结构体和一个class_rw_t结构体。
- 在编译期间,class_ro_t结构体就已经确定,objc_class中的bits的data部分存放着该结构体的地址。
- 在runtime运行之后,具体说来是在运行runtime的realizeClass 方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,再将当前类的分类的这些属性、方法等拷贝到其中并且更新data部分,换成class_rw_t结构体的地址。
- 类对象的获取方法
[NSObject class];
- 元类对象的获取方式
object_getClass([NSObject class])
既然说到三种对象 肯定还会牵扯出最最著名的isa superclass走向图
方法调用 本质 就是 消息
消息接受者 - 消息编号 - 参数
比如一个类叫Person 有实例方法eat和类方法run 实例了一个叫 person的变量
- 实例方法调用
objc_msgsend(person,sel_registerName("eat"));
objc_msgsend(person,@select(eat));
两种写法一样 - 类方法调用
objc_msgsend(objc_getClass("Person"),sel_registerName("run"));
isa的干啥的
实例对象的所有方法 协议 属性都存在 「类」方法里, 类的 类方法 又存在元类里
他们之间 是通过isa 串起来的。
instance的isa指向class
当调用对象方法时 通过instance 的isa找到class,再找到对象方法的实现进行调用
class的isa指向meta-class
当调用类方法时 通过class的isa找到meta-class,最后找到类方法的实现进行调用
superclass
举例说明 存在 :
@interface Person : NSObject
@interface Son : Person
如果 son 要调用 NSObject的实例方法
-(instancetype)init
方法[son init];
- 首先son 实例方法 通过isa 找到 自己的类方法
- 再通过 superclass指针 找到Person类对象
- 再通过Person的superClass指针找到 NSObject的类对象 才能找到 init方法 然后进行调用
调用类方法+(void)load
以此类推 类方法的调用,元类里的 superClass 指向的是它父类的元类对象
- instance 的isa 指向class
- class 的isa 指向meta-class
- meta-class 的isa 指向基类的meta-class
- class 的superclass 指向父类的 class如果没有父类指向nil
- meta-class 的superclass指向父类的meta-class
- 基类的meta-class 的 superclass 指向基类的class
instance 方法调用 isa 指向class 如果找不到 通过superclass 去父类找
class 方法调用 isa 找到meta-class 找不到 就通过superclass 找父类
补充
按照之前上面说的 那实例对象的isa 应该指向的是 类对象 但是地址为什么不一样呢?
// ISA()方法用于返回类指针
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
// ISA_MASK的值是0x00007ffffffffff8ULL
// 通过按位与的方式获取到类指针,ISA_MASK中对应的isa.bits中类指针的位数,均为1
return (Class)(isa.bits & ISA_MASK);
#endif
}
类对象的地址 和 ISA_MASK做了一个& 操作 咱们来看看 ISA_MASK 是什么
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
arm64 是ios用的值
x86 64 是macos用的值
调试看下结果
(lldb) p (long)bo->isa
(long) $9 = 8303516107936841
(lldb) p/x bo->isa
(Class) $10 = 0x001d800100001449 Brother
(lldb) p/x [bo class]
(Class) $11 = 0x0000000100001448 Brother
(lldb) p/x 0x001d800100001449 & 0x00007ffffffffff8
(long) $12 = 0x0000000100001448
(lldb)
isa & ISA_MASK 确实得到了类的地址
现在通过类isa指针去找元类试试看
错误:成员引用基类型“Class”不是结构或联合
既然它觉得咱们输出 那咱们搞个相同的结构体 接一下 原来的结构体这样就可以输出了
然后和之前一样 即可得到元类的地址其实也是类的isa指针地址 & ISA_MASK
ISA 确定了 那superClass呢
superclass 并没有做处理直接就可以找到父类
先到这后面想起来还有什么相关的再补充
谨以此记录学习的点点滴滴