美文网首页
iOS runtime源码分析 - Class

iOS runtime源码分析 - Class

作者: 某年某月某日晴 | 来源:发表于2020-09-23 21:05 被阅读0次

Runtime源码下载

Runtime的API文档:描述了Objective-C runtime动态库提供的一些函数和数据结构。

iOS上关于Runtime的源码其实就是objc4,我们在objc4源码上能看到最新版本为objc-781

源码地址打开后是这个样子的
下载解压后打开objc.xcodeproj我们就可以愉快的浏览源码了
解压后这个样子

Class的本质

我们知道不管是类对象还是元类对象,类型都是Class,class和mete-class的底层都是objc_class结构体的指针,内存中就是结构体,我们来通过源码探寻下Class的本质。

Class objectClass = [NSObject class];        
Class objectMetaClass = object_getClass([NSObject class]);

搜索objc_class结构体的定义,我们可以发现有两个文件中都包含了objc_class的定义,一个是objc-runtime-new.h,一个是objc-runtime-old.h
我们忽略old直接看new,因为old是objc2之前的用法,现在已经弃用.

struct objc_class : objc_object {
    // 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

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        ASSERT(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        ASSERT(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }

我们发现这个结构体继承 objc_object 并且结构体内有一些函数,因为这是c++结构体,在c上做了扩展,因此结构体中可以包含函数。我们来到objc_object内,截取部分代码

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

我们发现objc_object中有一个isa指针,那么objc_class继承objc_object,也就同样拥有一个isa指针.

isa_t isa

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

isa_t是个联合体,我们只关心它的Class cls的联合部分。而Class又是个objc_class *。每个对象里面有个isa的成员变量,指向它代表一个类的东西。从OC的角度看,每个对象都能从isa中寻找到它的类的相关信息。

objc_object与 objc_class关系

Runtime中定义了一个叫做 objc_object 的结构体,代表OC层的对象
定义了一个objc_class的结构体,代表OC层的类,可以清楚看到, objc_class是继承objc_object的,因此,类也是一个objc_object的对象

Class superclass

直接附一张实例,类,元类之间isa和superclass的关系图


虚线 isa指针,实线superclass

cache_t

用散列表(哈希表)来缓存曾经调用过的方法,用于加速方法的调用

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
......
#else
#error Unknown cache mask storage type.
#endif
    
#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;
......
};

class_data_bits_t

是存储类的方法、属性、协议等信息的地方

struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;
......
    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() {
        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中只有一个bits变量,从注释中得知里面存着以FAST_开头的定义的flags,例如FAST_HAS_DEFAULT_RR、FAST_CACHE_HAS_CXX_DTOR等等。
class_data_bits_t结构体里并没有看到方法、属性相关的内容,那么我们之前了解到的,类中存储的类的成员变量信息,实例方法,属性名等这些信息在哪里呢。通过data()方法可以看到bits变量中还存有一个class_rw_t类型的data指针。

我们来到class_rw_t中,截取部分代码,我们发现class_rw_t中存储着方法列表,属性列表,协议列表等内容。

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

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;
......
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_t *>();
    }

    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>()->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }
    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};
        }
    }
};

在class_rw_t结构体中我们找到了methods()、properties()、protocols()方法,获取的便是方法列表,属性列表,协议列表.
那么成员变量存在哪里呢,这时候我们发现了一个ro方法,会返回一个class_ro_t的结构体,我们来到class_ro_t内查看,找到了成员变量ivars。

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;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    method_list_t *baseMethods() const {
        return baseMethodList;
    }

    class_ro_t *duplicate() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
            return ro;
        } else {
            size_t size = sizeof(*this);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            return ro;
        }
    }
}

最后总结通过一张图进行总结

image

在class_ro_t结构体中其实有很多的成员变量和方法,其实class_rw_t中的ro,在类的编译期就已经确定了属性、方法以及要遵循的协议,objc_class中的bits中的data部分存放了ro信息。而在runtime运行之后,准确的说是在运行runtime的realizeClass方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,并且更新了data部分。换成了class_rw_t结构体的地址。

遗留待研究问题:

1,分类的底层结构
2,实例方法和类方法区别,相应存储位置

参考资料:
iOS源码
iOS底层原理总结 - 文集
重学OC第四篇:类结构之bits

相关文章

网友评论

      本文标题:iOS runtime源码分析 - Class

      本文链接:https://www.haomeiwen.com/subject/mkxayktx.html