Class解读

作者: 偶尔登南山 | 来源:发表于2019-04-15 11:58 被阅读5次

    类的声明

    首先我们在runtime.h文件中看到objc_class的结构声明:

    
    /// 类的声明结构
    
    struct objc_class {
    
    Class _Nonnull isa OBJC_ISA_AVAILABILITY;//isa 指针
    
    #if !__OBJC2__
    
    Class _Nullable super_class OBJC2_UNAVAILABLE;//指向父类的指针
    
    const char * _Nonnull name OBJC2_UNAVAILABLE;//类名
    
    long version OBJC2_UNAVAILABLE;//版本
    
    long info OBJC2_UNAVAILABLE;//其它信息
    
    long instance_size OBJC2_UNAVAILABLE;//实例变量空间大小
    
    struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;//成员变量列表
    
    struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;//方法列表
    
    struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;//方法缓存列表
    
    struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;//协议列表
    
    #endif
    
    } OBJC2_UNAVAILABLE;
    
    /* Use `Class` instead of `struct objc_class *` */
    
    

    以上是runtime暴露给我们的有关Class的接口,而在objc-runtime-new.h文件中有objc_class结构的完整声明:

    
    // MARK: - class的完整声明结构
    
    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,内部存储方法,属性,遵循的协议等
    
    // class_rw_t指针
    
    class_rw_t *data() {
    
    return bits.data();
    
    }
    
    // set bits
    
    void setData(class_rw_t *newData) {
    
    bits.setData(newData);
    
    }
    
    ...下面未列出
    
    }
    
    

    可以看出,objc_class中有几个比较重要的成员变量和函数:

    superclass

    指向父类的指针

    cache_t

    cache里面存的是指针和虚表,为了方法调用时,快速查询,提高效率.其结构声明如下:

    
    // MARK: - cache_t结构声明
    
    struct cache_t {
    
    struct bucket_t *_buckets;
    
    mask_t _mask;
    
    mask_t _occupied;
    
    ...下面未列出
    
    }
    
    
    bucket_t
    
    // MARK: - bucket_t声明结构
    
    struct bucket_t {
    
    private:
    
    // IMP-first is better for arm64e ptrauth and no worse for arm64.
    
    // SEL-first is better for armv7* and i386 and x86_64.
    
    #if __arm64__
    
    MethodCacheIMP _imp;
    
    cache_key_t _key;
    
    #else
    
    cache_key_t _key;
    
    MethodCacheIMP _imp;
    
    #endif
    
    public:
    
    inline cache_key_t key() const { return _key; }
    
    inline IMP imp() const { return (IMP)_imp; }
    
    inline void setKey(cache_key_t newKey) { _key = newKey; }
    
    inline void setImp(IMP newImp) { _imp = newImp; }
    
    void set(cache_key_t newKey, IMP newImp);
    
    };
    
    

    这个bucket_t类似于一个hash表,其没一个cache_key_t对应一个imp.其关系如下图:

    bucket_t
    class_data_bits_t结构
    
    // MARK: - class_data_bits_t声明结构
    
    struct class_data_bits_t {
    
    class_rw_t* data() {
    
    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;
    
    }
    
    ...其它未列出
    
    }
    
    

    class_data_bits_t中成员函数 data()函数,返回一个class_rw_t结构指针;

    class_rw_t
    
    // MARK: - 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;
    
    #if SUPPORT_INDEXED_ISA
    
    uint32_t index;
    
    #endif
    
    void setFlags(uint32_t set)
    
    {
    
    OSAtomicOr32Barrier(set, &flags);
    
    }
    
    void clearFlags(uint32_t clear)
    
    {
    
    OSAtomicXor32Barrier(clear, &flags);
    
    }
    
    // set and clear must not overlap
    
    void changeFlags(uint32_t set, uint32_t clear)
    
    {
    
    assert((set & clear) == 0);
    
    uint32_t oldf, newf;
    
    do {
    
    oldf = flags;
    
    newf = (oldf | set) & ~clear;
    
    } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    
    }
    
    };
    
    

    class_rw_t中的成员变量class_ro_t:

    class_ro_t
    
    // MARK: - class_ro_t结构声明
    
    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_ro_t有method_list_t,protocol_list_t,ivar_list_t,property_list_t等成员变量这些成员变量的作用是存储编译后已确定的一些信息.

            class_rw_t中同样有method_array_t(方法列表),property_array_t(属性列表),protocol_array_t(协议列表),这三者均继承自list_array_tt,list_array_tt可以扩充,内部分别存储的是method_list_t,property_list_t和protocol_list_t;这个三个成员变量主要是方便在运行时为类提供拓展能力.

    编译后class内存布局

    
    // Person
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Person : NSObject
    
    - (void)name;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
    
    Class newClass = objc_allocateClassPair(objc_getClass("NSObject"), "newClass", 0);
    
    objc_registerClassPair(newClass);
    
    id newObject = [[newClass alloc]init];
    
    NSLog(@"%s",class_getName([newObject class]));
    
    NSLog(@"Hello, World!");
    
    Person *person = [Person new];
    
    [person name];
    
    [person name];
    
    Class cls = [Person class];
    
    }
    
    return 0;
    
    }
    
    

    我们来看cls这个class在编译后的内存布局,首先我们在main入口函数里面继续初始化一个Class cls = [Person class];然后运行一下获取当前cls的内存地址,然后将断点放过.

    Person class address

    然后在_objc_init()函数(runtime初始化之前)加一个断点:

    _objc_init()

    用lldb命令调试一下:

    cls memory layout

            通过上图我们可以看到,name("Person")和baseMethodList这两个是有值,其它都为0x0000000000000000空指针,这个也反映出我们在Person中的最初定义:只有一个- (void)name方法,没有协议,成员变量,属性等.

    我们从baseMethodList方法列表中第一个method_t

    name func

    这个method_t正好对应我们定义的name方法;而baseMethodList里面也就有1个方法:

    baseMethodList count

    类的实现realizeClass

    realizeClass是类初始化函数,类的运行时初始化都要调用此函数.

    
    // MARK: - 类的初始化
    
    /***********************************************************************
    
    * realizeClass
    
    * Performs first-time initialization on class cls,
    
    * including allocating its read-write data.
    
    * Returns the real class structure for the class.
    
    * Locking: runtimeLock must be write-locked by the caller
    
    **********************************************************************/
    
    static Class realizeClass(Class cls)
    
    {
    
    runtimeLock.assertLocked();
    
    const class_ro_t *ro;
    
    class_rw_t *rw;
    
    Class supercls;
    
    Class metacls;
    
    bool isMeta;
    
    if (!cls) return nil;
    
    if (cls->isRealized()) return cls;
    
    assert(cls == remapClass(cls));
    
    // fixme verify class is not in an un-dlopened part of the shared cache?
    
    ro = (const class_ro_t *)cls->data();
    
    if (ro->flags & RO_FUTURE) {
    
    // This was a future class. rw data is already allocated.
    
    rw = cls->data();
    
    ro = cls->data()->ro;
    
    cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    
    } else {
    
    // Normal class. Allocate writeable class data.
    
    rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
    
    rw->ro = ro;
    
    rw->flags = RW_REALIZED|RW_REALIZING;
    
    cls->setData(rw);
    
    }
    
    isMeta = ro->flags & RO_META;
    
    rw->version = isMeta ? 7 : 0; // old runtime went up to 6
    
    // Choose an index for this class.
    
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    
    cls->chooseClassArrayIndex();
    
    if (PrintConnecting) {
    
    _objc_inform("CLASS: realizing class '%s'%s %p %p #%u",
    
    cls->nameForLogging(), isMeta ? " (meta)" : "",
    
    (void*)cls, ro, cls->classArrayIndex());
    
    }
    
    // Realize superclass and metaclass, if they aren't already.
    
    // This needs to be done after RW_REALIZED is set above, for root classes.
    
    // This needs to be done after class index is chosen, for root metaclasses.
    
    supercls = realizeClass(remapClass(cls->superclass));
    
    metacls = realizeClass(remapClass(cls->ISA()));
    
    #if SUPPORT_NONPOINTER_ISA
    
    // Disable non-pointer isa for some classes and/or platforms.
    
    // Set instancesRequireRawIsa.
    
    bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    
    bool rawIsaIsInherited = false;
    
    static bool hackedDispatch = false;
    
    if (DisableNonpointerIsa) {
    
    // Non-pointer isa disabled by environment or app SDK version
    
    instancesRequireRawIsa = true;
    
    }
    
    else if (!hackedDispatch && !(ro->flags & RO_META) &&
    
    0 == strcmp(ro->name, "OS_object"))
    
    {
    
    // hack for libdispatch et al - isa also acts as vtable pointer
    
    hackedDispatch = true;
    
    instancesRequireRawIsa = true;
    
    }
    
    else if (supercls && supercls->superclass &&
    
    supercls->instancesRequireRawIsa())
    
    {
    
    // This is also propagated by addSubclass()
    
    // but nonpointer isa setup needs it earlier.
    
    // Special case: instancesRequireRawIsa does not propagate
    
    // from root class to root metaclass
    
    instancesRequireRawIsa = true;
    
    rawIsaIsInherited = true;
    
    }
    
    if (instancesRequireRawIsa) {
    
    cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    
    }
    
    // SUPPORT_NONPOINTER_ISA
    
    #endif
    
    // Update superclass and metaclass in case of remapping
    
    cls->superclass = supercls;
    
    cls->initClassIsa(metacls);
    
    // Reconcile instance variable offsets / layout.
    
    // This may reallocate class_ro_t, updating our ro variable.
    
    if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    
    // Set fastInstanceSize if it wasn't set already.
    
    cls->setInstanceSize(ro->instanceSize);
    
    // Copy some flags from ro to rw
    
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
    
    cls->setHasCxxDtor();
    
    if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
    
    cls->setHasCxxCtor();
    
    }
    
    }
    
    // Connect this class to its superclass's subclass lists
    
    if (supercls) {
    
    addSubclass(supercls, cls);
    
    } else {
    
    addRootClass(cls);
    
    }
    
    // Attach categories
    
    methodizeClass(cls);
    
    return cls;
    
    }
    
    

            下面我们来验证class_ro_t确定时机.还是之前的步骤,先编译一次获取到cls的内存地址.在static Class realizeClass(Class cls){}入口添加一个条件断点,运行:

    condition breakpoint

    然后lldb调试cls:

    访问 class_data_bits_t 指针的内容:(class_data_bits_t) $2755 = (bits = 4294971696)

    获取 class_rw_t:

    
    (class_rw_t) $2757 = {
    
    flags = 128
    
    version = 8
    
    ro = 0x0000000000000008
    
    methods = {
    
    list_array_tt<method_t, method_list_t> = {
    
    = {
    
    list = 0x0000000000000000
    
    arrayAndFlag = 0
    
    }
    
    }
    
    }
    
    properties = {
    
    list_array_tt<property_t, property_list_t> = {
    
    = {
    
    list = 0x0000000100000f84
    
    arrayAndFlag = 4294971268
    
    }
    
    }
    
    }
    
    protocols = {
    
    list_array_tt<unsigned long, protocol_list_t> = {
    
    = {
    
    list = 0x0000000100001110
    
    arrayAndFlag = 4294971664
    
    }
    
    }
    
    }
    
    firstSubclass = nil
    
    nextSiblingClass = nil
    
    demangledName = 0x0000000000000000 <no value available>
    
    }
    
    
    class_rw_t

            我们可以看到class_rw_t中的成员变量ro = 0x0000000000000008在运行前已经有值了.获取一下ro:

    
    (lldb) p $2757.ro
    
    (const class_ro_t *) $2758 = 0x0000000000000008
    
    

    由于lldb输出class_ro_t报错:

    
    (lldb) p *$2758
    
    error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory
    
    

    我们直接单步断点,往下走两步,直接获取ro的值:

    class_ro_t

            我们发现const指针 class_ro_t已经在编译期把类的相关信息(方法,属性,成员变量,协议)确定了.而类的初始化方法执行之后,编译期的ro赋值给了运行期的rw的成员变量ro了.

    
    ro = (const class_ro_t *)cls->data();
    
    if (ro->flags & RO_FUTURE) {
    
    // This was a future class. rw data is already allocated.
    
    rw = cls->data();
    
    ro = cls->data()->ro;
    
    cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    
    } else {
    
    // 将ro等信息赋值给class_rw_t
    
    // Normal class. Allocate writeable class data.
    
    rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
    
    rw->ro = ro;
    
    rw->flags = RW_REALIZED|RW_REALIZING;
    
    cls->setData(rw);
    
    }
    
    

    我们知道class_rw_t中除了class_ro_t成员变量外,还有以下三个成员变量:

    
    method_array_t methods;
    
    property_array_t properties;
    
    protocol_array_t protocols;
    
    

            这三个成员变量的赋值操作在static void methodizeClass(Class cls)()函数中,分别遍历ro中的方法,协议,属性列表分别加入到class_rw_t的methods,protocols,properties中.

    
    // MARK: - 将ro中的方法,属性,协议等列表添加到rw中对应的三个成员变量列表中
    
    /***********************************************************************
    
    * methodizeClass
    
    * Fixes up cls's method list, protocol list, and property list.
    
    * Attaches any outstanding categories.
    
    * Locking: runtimeLock must be held by the caller
    
    **********************************************************************/
    
    static void methodizeClass(Class cls)
    
    {
    
    runtimeLock.assertLocked();
    
    bool isMeta = cls->isMetaClass();
    
    auto rw = cls->data();
    
    auto ro = rw->ro;
    
    // Methodizing for the first time
    
    if (PrintConnecting) {
    
    _objc_inform("CLASS: methodizing class '%s' %s",
    
    cls->nameForLogging(), isMeta ? "(meta)" : "");
    
    }
    
    // 遍历ro中方法将其加入到rw methods中
    
    // Install methods and properties that the class implements itself.
    
    method_list_t *list = ro->baseMethods();
    
    if (list) {
    
    prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
    
    rw->methods.attachLists(&list, 1);
    
    }
    
    // 遍历ro中属性列表将其加入到 rw properties中
    
    property_list_t *proplist = ro->baseProperties;
    
    if (proplist) {
    
    rw->properties.attachLists(&proplist, 1);
    
    }
    
    // 遍历ro中协议列表将其加入到 rw protocols中
    
    protocol_list_t *protolist = ro->baseProtocols;
    
    if (protolist) {
    
    rw->protocols.attachLists(&protolist, 1);
    
    }
    
    // Root classes get bonus method implementations if they don't have
    
    // them already. These apply before category replacements.
    
    if (cls->isRootMetaclass()) {
    
    // root metaclass
    
    addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
    
    }
    
    // Attach categories.
    
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    
    attachCategories(cls, cats, false /*don't flush caches*/);
    
    if (PrintConnecting) {
    
    if (cats) {
    
    for (uint32_t i = 0; i < cats->count; i++) {
    
    _objc_inform("CLASS: attached category %c%s(%s)",
    
    isMeta ? '+' : '-',
    
    cls->nameForLogging(), cats->list[i].cat->name);
    
    }
    
    }
    
    }
    
    if (cats) free(cats);
    
    #if DEBUG
    
    // Debug: sanity-check all SELs; log method list contents
    
    for (const auto& meth : rw->methods) {
    
    if (PrintConnecting) {
    
    _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
    
    cls->nameForLogging(), sel_getName(meth.name));
    
    }
    
    assert(sel_registerName(sel_getName(meth.name)) == meth.name);
    
    }
    
    #endif
    
    }
    
    

    总结

            编译期:通过objc_class->data()获取class_ro_t,class的method,property,protocol,ivar等已确定被赋值给class_ro_t,class_to_t只读,说明编译期类的内存布局已固定.
            运行期:通过realizeClass()类初始化函数,给class_rw_t分配空间,将class_ro_t赋值给class_rw_t的成员变量ro.将ro中的方法,协议,属性列表分别加入到class_rw_t的methods,protocols,properties中.class_rw_t提供了运行期这些成员变量的扩展性.

    编译后源码库

    编译后的源码放在Github, 如果对你有帮助,请给一个star吧!

    博客地址&相关文章

    博客地址: https://waitwalker.cn/

    系列文章:

    1. Runtime源码编译

    2. objc_object解读

    3. Method解读

    4. Class解读

    5. Ivar objc_property_t Protocol解读

    6. Block解读

    7. Retain&Release解读

    8. Autorelease解读

    9. Weak解读

    10. msgSend()解读

    相关文章

      网友评论

        本文标题:Class解读

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