iOS底层探索四-类的原理

演示文稿2.png

前言

阅读以下博文有助于更好的连贯理解本篇内容

1、iOS底层探索一-对象原理(上)

2、iOS底层探索二-对象原理(中)

3、iOS底层探索三-对象原理(下)

一、 isa 分析到元类

1、元类

对象原理三文篇中,对象isa & ISA_MASK得到shiftcls(类),如果按照分析对象流程继续分析shiftcls(类)会得到什么?

image.png

2、根元类

类的isa & ISA_MASK得到对象的元类,那么元类的isa & ISA_MASK会得到什么?可以无限套娃一直开辟内存吗?

image.png

元类isa & ISA_MASK得到一个新的内存地址:$3, $3指向NSObject,但是$3$4(根类:NSObject)的地址并不相同,而和 $5的元类的内存地址($6)相同。苹果称$6根元类$3为对象的根元类

根元类:$3isa & ISA_MASK得到的内存地址:$4根元类:$3自身相同,说明类的内存地址并不能无限开辟。

3、isa 链路图

苹果官方链路图:

isa流程图.png

  • isa:一个指向该类定义的指针,该对象是该类定义的实例
  • superclass:返回接收方父类的类对象

图中有两条链路图:

isa链路图

  1. Subclass子类对象isa -> 子类类isa-> 子类元类isa -> 根元类isa -> 根元类isa自身

  2. SuperClass父类对象isa -> 父类类isa-> 父类元类isa -> 根元类isa -> 根元类isa自身

  3. Rootclass根类对象isa -> 根类类isa -> 根元类isa -> 根元类isa自身

superclass继承链

  1. 子类 -> 父类 -> 根类 -> nil

  2. 元类子类元类 -> 父类元类 -> 根元类 -> 根类 -> nil

苹果官方图不太容易理解,图解一下会更容易通途易懂

isa链路图

image.png

superclass继承链

image.png

二、类的结构

对象原理三文篇中知道 Class 类型是 objc_class 指针类型,objc_class 是一个结构体,因而Class的本质是结构体指针类型。Class 的结构体objc_class是如何定义的?

1、objc_class 定义

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
    ......
};
复制代码

结构体:objc_class包含四个成员变量:

  1. Class ISA:隐藏参数 ISA,
  2. Class superclass:父类
  3. cache_t cache:以前是缓存指针和虚函数表
  4. class_data_bits_t bits:Class_rw_t 和 自定义rr/alloc标志 数据

ISA指针在对象原理三文篇中已经详细分析,superclass是类的父类,这个很好理解。cache_t cacheclass_data_bits_t bits分别是什么呢?

2、 cache_t 定义

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };

 ......
}
复制代码

结构体 cache_t 包含两个成员变量:

  1. explicit_atomic<uintptr_t> _bucketsAndMaybeMask:指针类型,存放buckets的首地址
  2. 联合体 union:联合体是互斥的,有我无他
  • _maybeMask:当前的缓存区count
  • _flags:当前cache的可存储的buckets数量,默认是0
  • _occupied:当前cache的可存储的buckets数量,默认是0
  • _originalPreoptCache:初始时候的缓存(注意联合体互斥,如果有值,则同等级struct为空)

3、 class_data_bits_t 定义

struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit) const
    {
        return bits & bit;
    }

    // Atomically set the bits in `set` and clear the bits in `clear`.
    // set and clear must not overlap.
    void setAndClearBits(uintptr_t set, uintptr_t clear)
    {
        ASSERT((set & clear) == 0);
        uintptr_t newBits, oldBits = LoadExclusive(&bits);
        do {
            newBits = (oldBits | set) & ~clear;
        } while (slowpath(!StoreReleaseExclusive(&bits, &oldBits, newBits)));
    }

    void setBits(uintptr_t set) {
        __c11_atomic_fetch_or((_Atomic(uintptr_t) *)&bits, set, __ATOMIC_RELAXED);
    }

    void clearBits(uintptr_t clear) {
        __c11_atomic_fetch_and((_Atomic(uintptr_t) *)&bits, ~clear, __ATOMIC_RELAXED);
    }

public:

    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    void setData(class_rw_t *newData)
    {
        ASSERT(!data()  ||  (newData->flags & (RW_REALIZING | RW_FUTURE)));
        // Set during realization or construction only. No locking needed.
        // Use a store-release fence because there may be concurrent
        // readers of data and data's contents.
        uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
        atomic_thread_fence(memory_order_release);
        bits = newBits;
    }

    // Get the class's ro data, even in the presence of concurrent realization.
    // fixme this isn't really safe without a compiler barrier at least
    // and probably a memory barrier when realizeClass changes the data field
    const class_ro_t *safe_ro() const {
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }
    
    ......
}
复制代码

结构体 class_data_bits_t包含两个成员变量:

  1. class_rw_t* data():当前类在运行时添加的类中的属性、方法还有遵循的协议等信息
  2. const class_ro_t *safe_ro():当前类在编译期就已经确定的属性、方法以及遵循的协议等信息

4、class_ro_t 定义

struct class_ro_t{
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };
    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    
    ......
}
复制代码
  • roread-only 只读属性

5、class_rw_t 定义

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    class_ro_t_authed_ptr<const class_ro_t> ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;

    Class firstSubclass;
    Class nextSiblingClass;

    ......
}
复制代码

rwread-write可读可写属性

6、class_ro_t 和 class_rw_t 区别

每个类创建都包含class_ro_tclass_rw_t两个结构体,不同的是class_ro_t存储在编译期就已经确定的属性、方法以及遵循的协议等信息,class_rw_t存储在运行时添加的类中的属性、方法还有遵循的协议等信息,确切的说是调用runtime的realizeClassapi时生成class_rw_t结构体,该结构体包含了class_ro_t,并更新data部分,指向class_rw_t结构体的地址。

realizeClass调用之前:

image.png
realizeClass调用之后:

image.png

三、类的取值

定义类Person:

image.png

1、属性列表

获取Person类属性:

image.png

2、实例方法列表

获取Person实例方法(也称对象方法):
image.png

获取类属性实例方法发现,属性存放在属性列表preperty_list_t中,但是却没有类的成员变量,方法存放在方法列表method_list_t中,但是却没有类方法,那么成员变量类方法存储到哪里了?WWDC2020有解释,成员变量存储在class_ro_t,并且在类调用在realizeClass方法后copyclass_ro_tclass_rw_t中,那需要获取
class_rw_t中的class_ro_t存储的数据。

3、成员变量列表

获取Person成员变量:
image.png

根据WWDC2020引导,确实从class_rw_t结构体包含的class_ro_t结构体中获取到类的成员变量,但是为什么类的成员变量存储在class_ro_t,类的属性存储在class_rw_t?成员变量和属性有什么区别呢?

  • 成员变量:类的定义或者类的实现中,在{ }中所声明的变量都为成员变量。成员变量用于类内部(不会生成setter、getter方法),不需与外界接触的变量。
  • 实例变量特殊的成员变量,实例变量本质上就是成员变量,只是实例是针对类而言。实例是指类的声明过程中,成员变量的类型是类类型,这类的成员变量就是实例变量
  • 属性:是OC语言中的一个机制,在OC中用@property来声明一个属性,其实@property是一种语法糖,编译器会自动生成settergetter方法。

定义Person类:

image.png

底层实现:(文篇对象原理中,有详细介绍如何查看底层源文件)

image.png

源文件中属性是带有_的成员变量,并且自动生成setter、getter 方法。

5、类方法列表

通过获取类的实例方法(对象方法)发现,对象方法存储在类中,并且类中只存储了对象方法。实例方法存储在类中,类方法会不会存储在元类中呢?

获取Person类方法:

image.png

获取元类方法列表,类方法存储在元类的class_rw_t结构体中,为了将实例方法类方法重名问题区分开来。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享