类的结构与class_data_bits_t

对象在底层的结构是objc_object,类在底层的结构是objc_class,类结构中包括哪些信息。

类的内存结构

objc4-818.2的源码中,类的简要结构是这样的,包括isa(继承自objc_object),superclasscachebits

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             
    class_data_bits_t bits;  
}
复制代码

class_ro_t and class_rw_t

在了解ro和rw之前需要知道两个概念,clean memorydirty memory.

  1. clean memory是指加载之后不回发生改变的内存。
  2. dirty memory是指运行时会发生改变的内存。

dirty memory要比clean memory更昂贵,在存储的过程中,我们尽量把不会改变的保存在clean memory中,把会改变的部分存储在dirty memoryclass_ro_t属于clean memory,它是read only,从磁盘加载到内存就是固定的数据。class_rw_tdirty memory,从这里再分离出一个class_rw_ext_t的结构,这部分是改变比较多的部分,放在dirty memory中。

image.png

class_data_bits_t

想要找到methodlist, propertylist,ivarlist,protocollist等等的数据,应该保存在class_data_bits_t中,点击进入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);
    }
复制代码

此结构体中只有一个成员变量bits,其他的都是函数,可以看到有一个函数返回的class_rw_t,点击进入这个结构体中,在这里我们发现了获取我们想要的method_array_t,property_array_t,protocol_array_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;

private:
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;

    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

    void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
    }

    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);

public:
    void setFlags(uint32_t set)
    {
        __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
    }

    void clearFlags(uint32_t clear) 
    {
        __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
    }

    // 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_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }

    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_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }

    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_or_rw_ext)->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 *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};
复制代码

从类中取出class_data_bits_t

根据上面的探索,类的结构已经知道了,想要获取到class_data_bits_t,就要从类的首地址向下平移isa class cache_t三个大小的和,isa占8字节,class是指针,大小为8字节。cache_t大小不知道,我们需要探索cache_t的大小。

  1. cache_t点击进去,不看函数和静态变量,因为函数和静态变量不影响结构体的大小,它的结构如下:
struct cache_t {
    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;
    };

explicit_atomic<uintptr_t> _bucketsAndMaybeMask;类型是uintptr_t大小是8字节
union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;      //4
#if __LP64__
            uint16_t                   _flags;        //2
#endif
            uint16_t                   _occupied;     //2
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;   //8
    };
联合体:里面一个结构体和一个指针,其中只有一个占有内存。

typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
复制代码

mask_tuint32_t大小为4,uint16_t大小为2,所以联合体的大小为8字节。那么cache_t的大小为16字节。

  1. lldb计算cache_t的大小

类结构中,前两个为isaClass,大小都是8字节,那么cache_t就是从类的首地址向下偏移16个字节。再使用sizeof函数算出大小。

image.png

获取到cache_t的大小,我们就可以取到class_data_bits_t

image.png

class_data_bits_t中取出属性列表

(lldb) x/6gx SwwPerson.class
0x1000083a0: 0x0000000100008378 0x000000010036a140
0x1000083b0: 0x00000001003623c0 0x0000803400000000
0x1000083c0: 0x00000001016040a4 0x00000001000b9990
(lldb) p (class_data_bits_t *)0x1000083c0
(class_data_bits_t *) $1 = 0x00000001000083c0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001016040a0
(lldb) p $2->properties()
(const property_array_t) $3 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000082b8
      }
      arrayAndFlag = 4295000760
    }
  }
}
(lldb) p $3.list
(const RawPtr<property_list_t>) $4 = {
  ptr = 0x00000001000082b8
}
(lldb) p $4.ptr
(property_list_t *const) $5 = 0x00000001000082b8
(lldb) p *$5
(property_list_t) $6 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 4)
}
(lldb) p $6.get(0)
(property_t) $7 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $6.get(1)
(property_t) $8 = (name = "name1", attributes = "T@\"NSString\",C,N,V_name1")
(lldb) p $6.get(2)
(property_t) $9 = (name = "name2", attributes = "T@\"NSString\",C,N,V_name2")
(lldb) p $6.get(3)
(property_t) $10 = (name = "name3", attributes = "T@\"NSString\",C,N,V_name3")
(lldb) p $6.get(4)
Assertion failed: (i < count), function get, file /Users/renshuang/Desktop/Apple Source/objc4-818.2/runtime/objc-runtime-new.h, line 624.
复制代码

在源码中可以看到property_list_t的结构:

class property_array_t : 
    public list_array_tt<property_t, property_list_t, RawPtr>
{
    typedef list_array_tt<property_t, property_list_t, RawPtr> Super;

 public:
    property_array_t() : Super() { }
    property_array_t(property_list_t *l) : Super(l) { }
};

//这个才是我们真正想要的
struct property_t {
    const char *name;
    const char *attributes;
};

复制代码

继承自list_array_tt,这是一个二维数组,结构图如下:

image.png

class_data_bits_t中取出方法列表

这是我们类的结构,看看方法列表中都有什么
image.png

(lldb) p/x SwwPerson.class
(Class) $0 = 0x0000000100008700 SwwPerson
(lldb) p (class_data_bits_t *)0x0000000100008720
(class_data_bits_t *) $1 = 0x0000000100008720
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100629b70
(lldb) p $2->methods()
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008510
      }
      arrayAndFlag = 4295001360
    }
  }
}
(lldb) p $3.list
(const method_list_t_authed_ptr<method_list_t>) $4 = {
  ptr = 0x0000000100008510
}
(lldb) p $4.ptr
(method_list_t *const) $5 = 0x0000000100008510
(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 5)
}
(lldb) p $5.get(0).big()
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x4000000004).
The process has been returned to the state before expression evaluation.
(lldb) p $6.get(0).big()
(method_t::big) $7 = {
  name = "name1"
  types = 0x0000000100003d0a "@16@0:8"
  imp = 0x0000000100003b80 (KCObjcBuild`-[SwwPerson name1])
}
(lldb) p $6.get(1).big()
(method_t::big) $8 = {
  name = "setName1:"
  types = 0x0000000100003db8 "v24@0:8@16"
  imp = 0x0000000100003bb0 (KCObjcBuild`-[SwwPerson setName1:])
}
(lldb) p $6.get(2).big()
(method_t::big) $9 = {
  name = "name"
  types = 0x0000000100003d0a "@16@0:8"
  imp = 0x0000000100003b20 (KCObjcBuild`-[SwwPerson name])
}
(lldb) p $6.get(3).big()
(method_t::big) $10 = {
  name = ".cxx_destruct"
  types = 0x0000000100003da4 "v16@0:8"
  imp = 0x0000000100003be0 (KCObjcBuild`-[SwwPerson .cxx_destruct])
}

复制代码

去方法数组的过程和属性几乎一致,但是打印时会使用一个.big()才能打印出名字,因为method_t的结构和property_t不一样,里面多了一层big结构体。

简要结构
struct method_t {
    
    // The representation of a "big" method. This is the traditional
    // representation of three pointers storing the selector, types
    // and implementation.
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
}
复制代码

class_data_bits_t中取出协议列表

(lldb) p/x SwwPerson.class
(Class) $0 = 0x0000000100008738 SwwPerson
(lldb) p (class_data_bits_t *)0x0000000100008758
(class_data_bits_t *) $1 = 0x0000000100008758
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001007488d0
(lldb) p $2->protocols()
(const protocol_array_t) $3 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000084c8
      }
      arrayAndFlag = 4295001288
    }
  }
}
(lldb) p $3.list
(const RawPtr<protocol_list_t>) $4 = {
  ptr = 0x00000001000084c8
}
(lldb) p $4.ptr
(protocol_list_t *const) $5 = 0x00000001000084c8
(lldb) p *$5
(protocol_list_t) $6 = (count = 2, list = protocol_ref_t [] @ 0x00007fce7c0cdd28)
(lldb) p $6.list[0]
(protocol_ref_t) $7 = 4295002056
(lldb) p (protocol_t *)$7
(protocol_t *) $8 = 0x00000001000087c8
(lldb) p *$8
(protocol_t) $9 = {
  objc_object = {
    isa = {
      bits = 4298547400
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537318425
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003cc6 "SwwPersonDelegate"
  protocols = 0x00000001000083a0
  instanceMethods = 0x00000001000083b8
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000083f0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) 
复制代码

class_data_bits_t中取出成员变量

从上面的分析中得知成员变量是在ro中,所以我们从class_ro_t中取成员变量

(lldb) p/x SwwPerson.class
(Class) $0 = 0x0000000100008738 SwwPerson
(lldb) p (class_data_bits_t *)0x0000000100008758
(class_data_bits_t *) $1 = 0x0000000100008758
(lldb) p $1->safe_ro()
(const class_ro_t *) $3 = 0x00000001000084e8
(lldb) p *$3
(const class_ro_t) $4 = {
  flags = 388
  instanceStart = 8
  instanceSize = 32
  reserved = 0
   = {
    ivarLayout = 0x0000000100003cf5 "\U00000003"
    nonMetaclass = 0x0000000100003cf5
  }
  name = {
    std::__1::atomic<const char *> = "SwwPerson" {
      Value = 0x0000000100003ceb "SwwPerson"
    }
  }
  baseMethodList = 0x0000000100008530
  baseProtocols = 0x00000001000084c8
  ivars = 0x00000001000085c8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008630
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $4.ivars
(const ivar_list_t *const) $5 = 0x00000001000085c8
(lldb) p *$5
(const ivar_list_t) $6 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $6.get(0)
(ivar_t) $7 = {
  offset = 0x00000001000086a8
  name = 0x0000000100003f6b "n"
  type = 0x0000000100003dac "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $6.get(1)
(ivar_t) $8 = {
  offset = 0x00000001000086b0
  name = 0x0000000100003f6d "_name"
  type = 0x0000000100003dac "@\"NSString\""
  alignment_raw = 3
  size = 8
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享