对象的分类和他们之间的关系

对象的分类

实例对象 Instance 类对象 Class 元类对象 meta-class

三者之间的关系

首先说说三者的结构

  • 实例对象 Instance 上篇说过,通过clang 编译可得出结论结构体中「只」会存储
    1. isa
    2. 成员变量
  • 类对象 Class 也就是类 查看 objc_runtime_new.h 可见
    1. isa
    2. superclass
    3. cache_t 方法缓存
    4. class_data_bits_t 具体的类信息
  • 元类对象 Class 和类对对象的结构体相同但是元类对象中只
    1. isa
    2. superclass
    3. 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结构体。

  1. 在编译期间,class_ro_t结构体就已经确定,objc_class中的bits的data部分存放着该结构体的地址。
  2. 在runtime运行之后,具体说来是在运行runtime的realizeClass 方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,再将当前类的分类的这些属性、方法等拷贝到其中并且更新data部分,换成class_rw_t结构体的地址。
  3. 类对象的获取方法 [NSObject class];
  4. 元类对象的获取方式 object_getClass([NSObject class])

既然说到三种对象 肯定还会牵扯出最最著名的isa superclass走向图

在这里插入图片描述

方法调用 本质 就是 消息
消息接受者 - 消息编号 - 参数
比如一个类叫Person 有实例方法eat和类方法run 实例了一个叫 person的变量

  1. 实例方法调用objc_msgsend(person,sel_registerName("eat"));
    objc_msgsend(person,@select(eat));两种写法一样
  2. 类方法调用 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];

  1. 首先son 实例方法 通过isa 找到 自己的类方法
  2. 再通过 superclass指针 找到Person类对象
  3. 再通过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 并没有做处理直接就可以找到父类

先到这后面想起来还有什么相关的再补充

谨以此记录学习的点点滴滴

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值