美文网首页
类的内存布局之bits

类的内存布局之bits

作者: 答案不止一个 | 来源:发表于2020-09-14 15:15 被阅读0次

class_data_bits_t bits;

objc_class 中还有一个成员是bits。这个成员存储着什么数据呢,我们一下继续往探寻。

struct class_data_bits_t {
    // 表示objc_class是友元。objc_class可以访问class_data_bits_t的私有属性方法
    friend objc_class; 
    // Values are the FAST_ flags above.
    // uintptr_t 8字节的unsigned long  
    uintptr_t bits;
    ...
}

class_data_bits_t 含有一个成员bits 是一个8字节的数据。另外objc_class 是他的友元。在objc_clas中可以访问class_data_bits_t 的私有属性和方法

class_data_bits_t 的方法
#if __LP64__
// class is a Swift class from the pre-stable Swift ABI
#define FAST_IS_SWIFT_LEGACY    (1UL<<0)
// class is a Swift class from the stable Swift ABI
#define FAST_IS_SWIFT_STABLE    (1UL<<1)
// class or superclass has default retain/release/autorelease/retainCount/
//   _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference
#define FAST_HAS_DEFAULT_RR     (1UL<<2)
// data pointer
#define FAST_DATA_MASK          0x00007ffffffffff8UL //  0x00007ffffffffff8  二进制4到46位为1

// class_rw_t 后面会了解到存储着类的成员变量,属性,方法等数据

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;
}
WX20200913-215010@2x.png
  1. bits的4到46为存储着 class_rw_t *的类型的指针。存储的逻辑是将4-46位置0,然后在 | 新的指针数据(指针是8字节,字节对齐,其实地址是8的倍数,所以后三位必定为0)
FAST_IS_SWIFT_STABLE - FAST_IS_SWIFT_LEGACY

FAST_IS_SWIFT_STABLE: class is a Swift class from the stable Swift ABI (1UL<<1)
FAST_IS_SWIFT_LEGACY: class is a Swift class from the pre-stable Swift ABI (1UL<<0)

    bool getBit(uintptr_t bit) const{
        return bits & bit;
    }
    
    bool isAnySwift() {
        return isSwiftStable() || isSwiftLegacy();
    }
    bool isSwiftStable() {
        return getBit(FAST_IS_SWIFT_STABLE);
    }
    void setIsSwiftStable() {
        setAndClearBits(FAST_IS_SWIFT_STABLE, FAST_IS_SWIFT_LEGACY);
    }
    bool isSwiftLegacy() {
        return getBit(FAST_IS_SWIFT_LEGACY);
    }
    void setIsSwiftLegacy() {
        setAndClearBits(FAST_IS_SWIFT_LEGACY, FAST_IS_SWIFT_STABLE);
    }
    // fixme remove this once the Swift runtime uses the stable bits
    bool isSwiftStable_ButAllowLegacyForNow() {
        return isAnySwift();
    }
    
FAST_HAS_DEFAULT_RR ( (1UL<<2))

class or superclass has default retain/release/autorelease/retainCount/_tryRetain/_isDeallocating/retainWeakReference/allowsWeak

#if FAST_HAS_DEFAULT_RR
    bool hasCustomRR() const {
        return !bits.getBit(FAST_HAS_DEFAULT_RR);
    }
    void setHasDefaultRR() {
        bits.setBits(FAST_HAS_DEFAULT_RR);
    }
    void setHasCustomRR() {
        bits.clearBits(FAST_HAS_DEFAULT_RR);
    }
#else
    bool hasCustomRR() const {
        return !(bits.data()->flags & RW_HAS_DEFAULT_RR);
    }
    void setHasDefaultRR() {
        bits.data()->setFlags(RW_HAS_DEFAULT_RR);
    }
    void setHasCustomRR() {
        bits.data()->clearFlags(RW_HAS_DEFAULT_RR);
    }
#endif
class_data_bits_t中存储的数据

所以,类的内存布局中:

  1. 1-2的0/1位则表示这是否swift 类,以及是否是 stable Swift ABI,还是 the pre-stable Swift ABI。(最多只会有一个生效)
  2. 第3位0/1位表示是否有默认的retain/release/autorelease/retainCount (在 LP64 最终的定义, 如果是在非 LP64。这是存储在 class_rw_t 的 flag 的对应位中)
  3. 从第4位开始,存储着 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;   // 2
    uint16_t witness; // 4
#if SUPPORT_INDEXED_ISA  // __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && ! __LP64__)
    uint16_t index; // 2
#endif
    explicit_atomic<uintptr_t> ro_or_rw_ext;  // 8
    Class firstSubclass; // 8  第一个子类
    Class nextSiblingClass; // 8  下一个兄弟类
private:
    // 声明一个类型别名。 using 别名 = 包括详细命名空间信息的具体的类型。 参考 c using的用法 
    // objc::PointerUnion 使用第一位来区分两种类型指针
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;
    ...
}
(lldb) p/x Person.class
(Class) $0 = 0x0000000100003468 Person
(lldb) x/8xg 0x0000000100003468
0x100003468: 0x0000000100003490 0x0000000100334140
0x100003478: 0x0000000101130b60 0x0003804400000007
0x100003488: 0x0000000101130694 0x00000001003340f0
0x100003498: 0x00000001003340f0 0x0000000101021420
(lldb) p (class_data_bits_t * )0x100003488
(class_data_bits_t *) $1 = 0x0000000100003488
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000101130690
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 4294979656
  }
  firstSubclass = nil  // 子类
  nextSiblingClass = NSUUID // 父类
}
(lldb) p sizeof($3)
(unsigned long) $4 = 32
ro_or_rw_ext

ro_or_rw_ext 从定义上来看,是一个两种类型指针公用的指针。根据第一个的0/1 来全判断。 0 表示是class_ro_t *; 1表示是 class_rw_ext_t 。rw表示的readwrite。ro表示的readonly。

class_rw_ext_t 中存储着对一个的ro,方法列表,属性列表,协议等信息
struct class_rw_ext_t {
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};
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;
}
class_rw_t/class_ro_t 的flag中存储着很多与和ABI有关的编译器设置
// Values for class_ro_t->flags
// These are emitted by the compiler and are part of the ABI.
// Note: See CGObjCNonFragileABIMac::BuildClassRoTInitializer in clang
// class is a metaclass
#define RO_META               (1<<0)
// class is a root class
#define RO_ROOT               (1<<1)
// class has .cxx_construct/destruct implementations
#define RO_HAS_CXX_STRUCTORS  (1<<2)
// class has +load implementation
// #define RO_HAS_LOAD_METHOD    (1<<3)
// class has visibility=hidden set
#define RO_HIDDEN             (1<<4)
// class has attribute(objc_exception): OBJC_EHTYPE_$_ThisClass is non-weak
#define RO_EXCEPTION          (1<<5)
// class has ro field for Swift metadata initializer callback
#define RO_HAS_SWIFT_INITIALIZER (1<<6)
...
#define RO_REALIZED           (1<<31)

// Values for class_rw_t->flags
// These are not emitted by the compiler and are never used in class_ro_t.
// Their presence should be considered in future ABI versions.
// class_t->data is class_rw_t, not class_ro_t
#define RW_REALIZED           (1<<31)
// class is unresolved future class
#define RW_FUTURE             (1<<30)
...
#define RW_META               RO_META // (1<<0)

flag 的不同0/1位代表着不同的信息,是否是mataclass,是否是根类等,具体可以再源码中查看。

class_rw_t 中的方法

获取方法列表。根据 ro_or_rw_ext 的is()判断当前是哪种指针,然后从对应的指针中获取方法列表.
其他的协议列表也是类似的

  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()};
        }
    }
类的内存布局之bits.png

WWDC20 Advancements in the Objective-C runtime

  1. clean memoery 加载后不会改变, 当内存紧张时,可以移除,当需要再次引入。
  2. dirty memory 运行时会改变的数据,在mac上可以选择 swap out dirty memory。 但是ios 不适用swap。
类结构的变化。

使用一个中间的class_rw_t 保存dirty menory,(运行时可以改变或者交换方法,协议,属性)。然后因为很多没有使用,有分离出 class_rw_ext_t 结构保存哪些改变了的数据。


class memory.png
Relative Mathod List

类的方法列表发生了改变。原先mathod 中的sel, signature, imp 存储的都是 64位的确定地址。因为其都是存储在同一个二进制文件中,所以,优化为存储 32位的偏移地址,而且偏移地址从image中读取之后是固定的,就不需要修改,所以成了clean memory。

不能修改imp的地址,那么实现swizze,采用的是一个全局的swizzes表,用来存储swizzes的数据

swizzing method list.png
tagged pointers

地址经常是没有完全使用的,高位和地位总会是0。所以在一些位置设置为特定的属性标签。
比如isa指针中第1位这是位1,表示这不是一个真正的对象指针(优化),然后就可以在其他的位置设置标签。

tagged Pointer on Intel.png tagged Pointer on arm64.jpeg
类的内存布局是有助于我们理解类的数据。但不要直接访问这些内存的数据,不同版本会有不同,使用api区获取

相关文章

  • 类的内存布局之bits

    class_data_bits_t bits; objc_class 中还有一个成员是bits。这个成员存储着什么...

  • OC底层原理-cache和bits探索

    类的属性存储 之前已经描述了类的存储,类的内存中存储了isa、superclass、cache、bits,前面已经...

  • C++ 对象内存布局

    虚函数, 虚基类 同时存在的时候, 对象内存布局的影响。 转自对象内存布局 (16) - CSDN博客 虚基类指针...

  • 类的内存布局之isa指针

    [Cls alloc] 在计算完大小,并且分配空间之后,就去执行initInstanceIsa。现在继续跟着源码看...

  • 五、类原理之bits

    1、指针平移 在介绍类的内存结构之前先介绍一下指针平移,在后面会用到相关知识。 这张图的意思呢就是定义一个整型数组...

  • 类原理探索-cache_t

    前言 前面的文章我们讨论了类的底层实现和通过内存平移的到来 class_data_bits_t 这个结构,也引出了...

  • 类的内存布局之cache_t

    在类的编译成cpp的文件中,了解到编译期数据,方法的存储以及类和元类的相关信息。现在则根据源码,了解具体的类的结构...

  • Runtime源码解析-类中bits

    Runtime源码解析-类中bits 首先我们再看一眼objc_class类的定义,本篇文章研究bits到底存储了...

  • 对象在内存中的布局

    参考文章:一个objc对象如何进行内存布局?(考虑有父类的情况) Objective-C内存布局 参考如下的3张图...

  • iOS底层原理之方法缓存

    方法缓存 class中有isa指针,superClass,cache方法缓存,bits具体的类信息. bits和F...

网友评论

      本文标题:类的内存布局之bits

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