iOS 底层探究之class_data_bits_t

  1. iOS 底层探究之alloc
  2. iOS 底层探究之isa 和 superClass

上篇分别研究了isa和supperClass的指针指向内存内容,提到isa指向类信息、元类信息,这篇来看看类信息和元类信息中都存储哪些内容。

类对象基本数据结构体

  1. Class对应的结构体是objc_class, 它的成员变量只有四个: isa、superClass、cache、bits,类对象的信息都存放在bits中。
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;

    // 下面都是方法
}
复制代码
  1. class_data_bits_t 结构体, 我们都知道static的数据都放在全局区,不占结构体内存。 所以class_data_bits_t就两个属性,一个_bucketsAndMaybeMask, 另外一个union,加起来16字节。 a. uintptr_t是unsigned long 8字节,所以_bucketsAndMaybeMask长度为8 b. union都是公用内存,_originalPreoptCache是一个指针,所以长度为8字节。 mask_t是uint32_t为4字节,uint16_t为2字节,在64位环境下,union的struct为8字节。所以union长度为8字节。
typedef unsigned long           uintptr_t; // 4字节

#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits 4字节
#else
typedef uint16_t mask_t; // 2 字节
#endif

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;
    };

    // 下面都是static
}
复制代码

3.class_data_bits_t 是一个struct,长度为8字节。bits中的数据主要存储在 data() 和 safe_ro()中,data()保存着类的成员变量、协议、方法、属性等数据。

 struct class_data_bits_t {
     class_rw_t* data() const {
         return (class_rw_t *)(bits & FAST_DATA_MASK);
     }
 
     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;
         }
     }
 }
复制代码

4.class_rw_t data()结构体

struct class_rw_t {
 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);
 }
 
 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 bits的内存地址,再进一步的获取类的相关信息。

获取class_rw_t结构体

struct class_rw_t保存着类的成员变量、属性、方法、协议,所以我们先获取到这个结构体。 1.获取LKXPerson类对象内存地址

(lldb) p/x LKXPerson.class
(Class) $0 = 0x00000001000084a0
复制代码

2.获取class_data_bits_t bits,从前面知道bits前面有三个变量共32字节, 所以 0x0000000100008368 + 0x20 = 0x0000000100008388, 对其做类型转换

(lldb) p/x 0x00000001000084a0 + 0x20
(long) $1 = 0x00000001000084c0
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000084c0
复制代码

3.获取struct class_rw_t

(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100a17490
复制代码

4. 3 变量保存的就是 c l a s s r w t 结构体,下面我们可以通过 3变量保存的就是class_rw_t结构体,下面我们可以通过 3(class_rw_t)来获取类的成员变量、属性、方法、协议。``

类信息class_ro_t

结构体class_ro_t从名称就可知,这部分是不可变的,这里保存着对象声明及实现的成员变量、成员属性、实例方法、协议。

  1. 通过$4(class_rw_t)的ro()获取class_ro_t结构体的指针:
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x00000001000081b0
复制代码
  1. 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;
    WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
}
复制代码
  1. 打印class_ro_t数据:
(lldb) p *$4
(const class_ro_t) $5 = {
  flags = 388
  instanceStart = 8
  instanceSize = 40
  reserved = 0
   = {
    ivarLayout = 0x0000000100003dbe "\U00000001\U00000012"
    nonMetaclass = 0x0000000100003dbe
  }
  name = {
    std::__1::atomic<const char *> = "LKXPerson" {
      Value = 0x0000000100003db4 "LKXPerson"
    }
  }
  baseMethods = {
    ptr = 0x00000001000081f8
  }
  baseProtocols = 0x0000000100008188
  ivars = 0x00000001000082d8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008360
  _swiftMetadataInitializer_NEVER_USE = {}
}
复制代码

成员变量

  1. 我们可通过ivars获取成员变量的内存地址:
// ivars的指针
(lldb) p $5.ivars 
(const ivar_list_t *const) $6 = 0x00000001000082d8
// ivars存储数据的首地址
(lldb) p *$6
(const ivar_list_t) $7 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
复制代码
  1. ivars最终得到长度为4的链表,表示有四个成员变量,我们打印成员变量:
(lldb) p $7.get(0)
(ivar_t) $8 = {
  offset = 0x0000000100008458
  name = 0x0000000100003ed7 "_age"
  type = 0x0000000100003e2c "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
  offset = 0x0000000100008460
  name = 0x0000000100003edc "_height"
  type = 0x0000000100003e38 "d"
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
  offset = 0x0000000100008468
  name = 0x0000000100003ee4 "_name"
  type = 0x0000000100003e2c "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(3)
(ivar_t) $11 = {
  offset = 0x0000000100008470
  name = 0x0000000100003eea "_hobby"
  type = 0x0000000100003e2c "@\"NSString\""
  alignment_raw = 3
  size = 8
}
复制代码
  1. 我们分析成员变量发现,定义的成员属性也会为其自动生成成员变量

baseProtocols

  1. 获取到协议链表的指针
(lldb) p $5->baseProtocols
(protocol_list_t *const) $12 = 0x0000000100008188
  Fix-it applied, fixed expression was: 
    $5.baseProtocols
复制代码
  1. 获取协议链表的内存地址:
(lldb) p *$12
(protocol_list_t) $13 = (count = 3, list = protocol_ref_t [] @ 0x00006000027af638)
复制代码
  1. 得到其遵守了三个协议,和我们定义(NSCopying, NSCoding, NSMutableCopying)的是一样,protocol_list_t在objc4中的定义,我智能获取它第一个协议的地址。
/// 定义
struct protocol_list_t {
    // count is pointer-sized by accident.
    uintptr_t count;
    protocol_ref_t list[0];
}
复制代码
  1. 获取协议列表指针:
(lldb) p $13.list[0]
(protocol_ref_t) $14 = 4295001376
复制代码

5.获取协议列表首地址:

(lldb) p (protocol_t *)$14
(protocol_t *) $15 = 0x0000000100008520
复制代码

6.获取所有协议:

(lldb) p $15[0]
(protocol_t) $16 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003d90 "NSCopying"
  protocols = nil
  instanceMethods = 0x0000000100008040
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008060
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $15[1]
(protocol_t) $17 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003d9a "NSCoding"
  protocols = nil
  instanceMethods = 0x0000000100008068
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000080a0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $15[2]
(protocol_t) $18 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003da3 "NSMutableCopying"
  protocols = nil
  instanceMethods = 0x00000001000080b0
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000080d0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
复制代码
  1. 通过分析“mangledName”,和LKXPerson遵守的协议是一样的。

baseProperties

  1. 获取成员属性的指针地址:
(lldb) p $5.baseProperties
(property_list_t *const) $18 = 0x0000000100008360
复制代码
  1. 因为struct property_list_t是一个链表,:
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
复制代码
  1. 所以我尝试通过一个item一个item获取:
(lldb) p $18.get(0)
(property_t) $19 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
  Fix-it applied, fixed expression was: 
    $18->get(0)
(lldb) p/x $18.get(1)
(property_t) $22 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
  Fix-it applied, fixed expression was: 
    $18->get(1)
(lldb) p/x $18.get(2)
Assertion failed: (i < count), function get, file objc-runtime-new.h, line 629.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
复制代码
  1. 获取第三个是就报错,说明自由三个成员变量,和我们定义的是一样多。

baseMethods

  1. 获取方法列表指针
(lldb) p $5.baseMethods
(const WrappedPtr<method_list_t, method_list_t::Ptrauth>) $24 = {
  ptr = 0x00000001000081f8
}
复制代码
  1. 在获取方法列表内存地址:
(lldb) p $24.ptr
(method_list_t *const) $25 = 0x00000001000081f8
复制代码
  1. 获取方法列表详细信息:
(lldb) p *$25
(method_list_t) $26 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 9)
}
复制代码

4.method_list_t也是一个结构体,通过对它的分析,发现实例方法时封装成method_t结构体变量保存在这个list中,method_t中有一个struct big保存着实例方法的方法名、参数、返回值、函数地址等,定义如下:

struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier>

struct method_t {
	struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
}
复制代码

5.获取所有方法

(lldb) p $26.get(0).big()
(method_t::big) $27 = {
  name = "sayNB"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a60 (KCObjcBuild`-[LKXPerson sayNB] at LKXTestBitsDemo.m:27)
}
(lldb) p $26.get(1).big()
(method_t::big) $28 = {
  name = "test1"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a90 (KCObjcBuild`-[LKXPerson test1] at LKXTestBitsDemo.m:45)
}
(lldb) p $26.get(2).big()
(method_t::big) $29 = {
  name = "test2"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003ac0 (KCObjcBuild`-[LKXPerson test2] at LKXTestBitsDemo.m:48)
}
(lldb) p $26.get(3).big()
(method_t::big) $30 = {
  name = "pTest"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003af0 (KCObjcBuild`-[LKXPerson pTest] at LKXTestBitsDemo.m:52)
}
(lldb) p $26.get(4).big()
(method_t::big) $31 = {
  name = "hobby"
  types = 0x0000000100003e3a "@16@0:8"
  imp = 0x0000000100003b80 (KCObjcBuild`-[LKXPerson hobby] at LKXTestBitsDemo.m:17)
}
(lldb) p $26.get(5).big()
(method_t::big) $32 = {
  name = "setHobby:"
  types = 0x0000000100003e0e "v24@0:8@16"
  imp = 0x0000000100003bb0 (KCObjcBuild`-[LKXPerson setHobby:] at LKXTestBitsDemo.m:17)
}
(lldb) p $26.get(6).big()
(method_t::big) $33 = {
  name = "name"
  types = 0x0000000100003e3a "@16@0:8"
  imp = 0x0000000100003b20 (KCObjcBuild`-[LKXPerson name] at LKXTestBitsDemo.m:16)
}
(lldb) p $26.get(7).big()
(method_t::big) $34 = {
  name = ".cxx_destruct"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003be0 (KCObjcBuild`-[LKXPerson .cxx_destruct] at LKXTestBitsDemo.m:25)
}
(lldb) p $26.get(8).big()
(method_t::big) $35 = {
  name = "setName:"
  types = 0x0000000100003e0e "v24@0:8@16"
  imp = 0x0000000100003b50 (KCObjcBuild`-[LKXPerson setName:] at LKXTestBitsDemo.m:16)
}
复制代码

6.方法列表中保存了所有实例方法,包括私有方法,还有成员属性自动生成的getter和setter方法,还有一个系统的C++析构方法。

获取元类的信息

  1. 获取元类内存地址中的isa,类信息前8个字节保存类信息地址,打印也是LKXPerson,可以确认是元类。
(lldb) x/4gx $0
0x1000084a0: 0x0000000100008478 0x0000000100802140
0x1000084b0: 0x0000000100a95820 0x0001803400000003
(lldb) po 0x0000000100008478
LKXPerson
复制代码
  1. 将元类内存地址与&上 0x00007ffffffffff8 就可以得到元类objc_class的内存地址
lldb) p/x 0x0000000100008478 & 0x00007ffffffffff8
(long) $37 = 0x0000000100008478
复制代码
  1. 然后通过移动20字节得到0x0000000100008360,即class_data_bits_t结构体指针地址,获取其指向内存地址:
(lldb) p/x 0x0000000100008478 + 0x20
(long) $38 = 0x0000000100008498
(lldb) p (class_data_bits_t *)$38
(class_data_bits_t *) $39 = 0x00000001000084a8
复制代码
  1. 获取class_rw_t结构体内存地址:
(lldb) p $39->data()
(class_rw_t *) $40 = 0x0000000100a28760
复制代码
  1. 获取类信息的class_ro_t结构体
(lldb) p $40->ro()
(const class_ro_t *) $42 = 0x00000001000080d8
复制代码
  1. 打印类信息class_ro_t结构体:
(lldb) p *$42
(const class_ro_t) $43 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "LKXPerson" {
      Value = 0x0000000100003db4 "LKXPerson"
    }
  }
  baseMethods = {
    ptr = 0x0000000100008120
  }
  baseProtocols = 0x0000000100008188
  ivars = nil
  weakIvarLayout = 0x0000000000000000
  baseProperties = nil
  _swiftMetadataInitializer_NEVER_USE = {}
}
复制代码
  1. 我们看到成员变量、成员属性、是nil,协议和方法列表有值。
  2. 我们获取协议列表信息,获取到由三个协议,和我们声明的个数一样的。其实改变申明的协议我们可以发现就是我们申明的。
(lldb) p $43.baseProtocols
(protocol_list_t *const) $44 = 0x0000000100008188
(lldb) p *$44
(protocol_list_t) $45 = (count = 3, list = protocol_ref_t [] @ 0x00006000027acfa8)
(lldb) p $45.list[0]
(protocol_ref_t) $46 = 4295001376
(lldb) p (protocol_t *)$46
(protocol_t *) $102 = 0x0000000100008520
(lldb) p $46[0]
(protocol_t) $46 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003d90 "NSCopying"
  protocols = nil
  instanceMethods = 0x0000000100008040
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008060
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $46[1]
(protocol_t) $48 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003d9a "NSCoding"
  protocols = nil
  instanceMethods = 0x0000000100008068
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000080a0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $46[2]
(protocol_t) $49 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003da3 "NSMutableCopying"
  protocols = nil
  instanceMethods = 0x00000001000080b0
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000080d0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
复制代码
  1. 我们class_ro_t结构体获取类方法列表信息,和我们声明的类方法是一样的:
(lldb) p $43.baseMethods
(const WrappedPtr<method_list_t, method_list_t::Ptrauth>) $50 = {
  ptr = 0x0000000100008120
}
(lldb) p $50.ptr
(method_list_t *const) $51 = 0x0000000100008120
(lldb) p *$51
(method_list_t) $52 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 4)
}
(lldb) p $52.get(0).big()
(method_t::big) $53 = {
  name = "say666"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x00000001000039a0 (KCObjcBuild`+[LKXPerson say666] at LKXTestBitsDemo.m:31)
}
(lldb) p $52.get(1).big()
(method_t::big) $54 = {
  name = "heiha"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x00000001000039d0 (KCObjcBuild`+[LKXPerson heiha] at LKXTestBitsDemo.m:35)
}
(lldb) p $52.get(2).big()
(method_t::big) $55 = {
  name = "testPlus1"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a00 (KCObjcBuild`+[LKXPerson testPlus1] at LKXTestBitsDemo.m:39)
}
(lldb) p $52.get(3).big()
(method_t::big) $56 = {
  name = "testPlus2"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a30 (KCObjcBuild`+[LKXPerson testPlus2] at LKXTestBitsDemo.m:42)
}
复制代码
  1. 获取类属性为空:
(lldb) p $40->properties()
(const property_array_t) $57 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = nil
      }
      arrayAndFlag = 0
    }
  }
}
复制代码
  1. 获取类方法,得到4个类方法,是我们前面声明的个数一样:
(lldb) p $40->methods()
(const method_array_t) $58 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008120
      }
      arrayAndFlag = 4295000352
    }
  }
}
(lldb) p $58.list
(const method_list_t_authed_ptr<method_list_t>) $59 = {
  ptr = 0x0000000100008120
}
(lldb) p $59.ptr
(method_list_t *const) $60 = 0x0000000100008120
(lldb) p *$60
(method_list_t) $61 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 4)
}
(lldb) p $61.get(0).big()
(method_t::big) $62 = {
  name = "say666"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x00000001000039a0 (KCObjcBuild`+[LKXPerson say666] at LKXTestBitsDemo.m:31)
}
(lldb) p $61.get(1).big()
(method_t::big) $63 = {
  name = "heiha"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x00000001000039d0 (KCObjcBuild`+[LKXPerson heiha] at LKXTestBitsDemo.m:35)
}
(lldb) p $61.get(2).big()
(method_t::big) $64 = {
  name = "testPlus1"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a00 (KCObjcBuild`+[LKXPerson testPlus1] at LKXTestBitsDemo.m:39)
}
(lldb) p $61.get(3).big()
(method_t::big) $65 = {
  name = "testPlus2"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a30 (KCObjcBuild`+[LKXPerson testPlus2] at LKXTestBitsDemo.m:42)
}
复制代码
  1. 获取类遵守的协议是三个和我们申明的一样:
(lldb) p $40->protocols()
(const protocol_array_t) $66 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008188
      }
      arrayAndFlag = 4295000456
    }
  }
}
(lldb) p $66.list.ptr
(protocol_list_t *const) $67 = 0x0000000100008188
(lldb) p *$67
(protocol_list_t) $68 = (count = 3, list = protocol_ref_t [] @ 0x00006000027ae038)
(lldb) 
复制代码
  1. 可以看到class_ro_t结构体和类信息中的协议和方法一模一样,所以可以确定元类中保存着类方法和协议。

获取实例方法

  1. 获取实例方法列表指针:
(lldb) p $3->methods()
(const method_array_t) $70 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000081f8
      }
      arrayAndFlag = 4295000568
    }
  }
}
复制代码
  1. 获取实例方法指针地址:
(lldb) p $70.list.ptr
(method_list_t *const) $71 = 0x00000001000081f8
复制代码

3.获取实例方法结构体内存地址:

(lldb) p *$71
(method_list_t) $72 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 9)
}
复制代码

4.发现有9个实例方法:

(lldb) p $72.get(0).big()
(method_t::big) $73 = {
  name = "sayNB"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a60 (KCObjcBuild`-[LKXPerson sayNB] at LKXTestBitsDemo.m:27)
}
(lldb) p $72.get(1).big()
(method_t::big) $74 = {
  name = "test1"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003a90 (KCObjcBuild`-[LKXPerson test1] at LKXTestBitsDemo.m:45)
}
(lldb) p $72.get(2).big()
(method_t::big) $75 = {
  name = "test2"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003ac0 (KCObjcBuild`-[LKXPerson test2] at LKXTestBitsDemo.m:48)
}
(lldb) p $72.get(3).big()
(method_t::big) $76 = {
  name = "pTest"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003af0 (KCObjcBuild`-[LKXPerson pTest] at LKXTestBitsDemo.m:52)
}
(lldb) p $72.get(4).big()
(method_t::big) $77 = {
  name = "hobby"
  types = 0x0000000100003e3a "@16@0:8"
  imp = 0x0000000100003b80 (KCObjcBuild`-[LKXPerson hobby] at LKXTestBitsDemo.m:17)
}
(lldb) p $72.get(5).big()
(method_t::big) $78 = {
  name = "setHobby:"
  types = 0x0000000100003e0e "v24@0:8@16"
  imp = 0x0000000100003bb0 (KCObjcBuild`-[LKXPerson setHobby:] at LKXTestBitsDemo.m:17)
}
(lldb) p $72.get(6).big()
(method_t::big) $79 = {
  name = "name"
  types = 0x0000000100003e3a "@16@0:8"
  imp = 0x0000000100003b20 (KCObjcBuild`-[LKXPerson name] at LKXTestBitsDemo.m:16)
}
(lldb) p $72.get(7).big()
(method_t::big) $80 = {
  name = ".cxx_destruct"
  types = 0x0000000100003e24 "v16@0:8"
  imp = 0x0000000100003be0 (KCObjcBuild`-[LKXPerson .cxx_destruct] at LKXTestBitsDemo.m:25)
}
(lldb) p $72.get(8).big()
(method_t::big) $81 = {
  name = "setName:"
  types = 0x0000000100003e0e "v24@0:8@16"
  imp = 0x0000000100003b50 (KCObjcBuild`-[LKXPerson setName:] at LKXTestBitsDemo.m:16)
}
复制代码

7.分析这里方法,包含声明的实例方法、私有方法,还有成员属性自动生成的getter和setter方法,系统的C++析构方法。

协议

  1. 获取协议列表指针地址
(lldb) p $3->protocols()
(const protocol_array_t) $82 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008188
      }
      arrayAndFlag = 4295000456
    }
  }
}
复制代码
  1. 获取协议列表内存地址:
(lldb) p $82.list.ptr
(protocol_list_t *const) $83 = 0x0000000100008188
复制代码
  1. 获取协议列表指针
(lldb) p $83.list[0]
(protocol_ref_t) $85 = 4295001376
  Fix-it applied, fixed expression was: 
    $83->list[0]
复制代码
  1. 获取协议首地址:
(lldb)  p (protocol_t *)$85
(protocol_t *) $90 = 0x0000000100008520
复制代码
  1. 打印协议:
(lldb) p $90[0]
(protocol_t) $91 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003d90 "NSCopying"
  protocols = nil
  instanceMethods = 0x0000000100008040
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008060
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $90[1]
(protocol_t) $92 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003d9a "NSCoding"
  protocols = nil
  instanceMethods = 0x0000000100008068
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000080a0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $90[2]
(protocol_t) $93 = {
  objc_object = {
    isa = {
      bits = 0
      cls = nil
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 0
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003da3 "NSMutableCopying"
  protocols = nil
  instanceMethods = 0x00000001000080b0
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000080d0
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
复制代码
  1. 通过协议的mangledName属性我们可以得到和遵守的协议时一样

类属性

  1. 我们为LKXPerson加上类属性:
@property (class, nonatomic, copy) NSString *clsProperty;
复制代码
  1. 获取类属性:
(lldb) p/x LKXPerson.class
(Class) $5 = 0x00000001000084b0
(lldb) x/4gx $5
0x1000084b0: 0x0000000100008488 0x0000000100810140
0x1000084c0: 0x0000000100b36e10 0x0001803400000007
(lldb) p/x 0x0000000100008488 & 0x00007ffffffffff8
(long) $6 = 0x0000000100008488
(lldb) p/x 0x0000000100008488 + 0x20
(long) $7 = 0x00000001000084a8
(lldb) p (class_data_bits_t *)$7
(class_data_bits_t *) $8 = 0x00000001000084a8
(lldb) p $8->data()
(class_rw_t *) $9 = 0x0000000100ada8f0
(lldb) p $9.properties()
(const property_array_t) $10 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008160
      }
      arrayAndFlag = 4295000416
    }
  }
}
  Fix-it applied, fixed expression was: 
    $9->properties()
(lldb) p $10.list.ptr
(property_list_t *const) $11 = 0x0000000100008160
(lldb) p *$11
(property_list_t) $12 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 1)
}
(lldb) p $12.get(0)
(property_t) $13 = (name = "clsProperty", attributes = "T@\"NSString\",C,N")
(lldb) p $9->ro()
(const class_ro_t *) $14 = 0x0000000100008178
(lldb) p *$14
(const class_ro_t) $15 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "LKXPerson" {
      Value = 0x0000000100003db6 "LKXPerson"
    }
  }
  baseMethods = {
    ptr = 0x0000000100008038
  }
  baseProtocols = 0x0000000100008138
  ivars = nil
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008160
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $15.baseProperties
(property_list_t *const) $17 = 0x0000000100008160
(lldb) p *$17
(property_list_t) $18 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 1)
}
(lldb) p $18.get(0)
(property_t) $19 = (name = "clsProperty", attributes = "T@\"NSString\",C,N")
(lldb) 
复制代码
  1. 可以看到元类中是保存类属性,分别在baseProperties和properties()中。

总结

通过对 struct class_rw_t 的分析,得出以下几点:

  1. 类对象对应的 struct class_rw_t 直接保存着类的实例方法(method_array_t *)、实例属性(property_array_t)、遵守的协议(protocol_array_t),这三个都是二维数组。
  2. 类的 struct class_rw_t 获取 const class_ro_t *ro() 将得到里面保存着类的成员变量(const ivar_list_t *)、实例属性(property_list_t *)、遵守的协议(protocol_list_t *)、实例方法(WrappedPtr < method_list_t, method_list_t::Ptrauth > ),但是这里的内存是不可变的,而且都是一维数组。
  3. 元类对象对应的 struct class_rw_t 直接保存着类的类方法(method_array_t *)、遵守的协议(protocol_array_t)、类属性(property_array_t),这两个都是二维数组。
  4. 元类的 struct class_rw_t 获取 const class_ro_t *ro() 将得到里面保存着遵守的协议(protocol_list_t *)、类方法(WrappedPtr < method_list_t, method_list_t::Ptrauth > )、类属性(property_list_t *)。
  5. 类的结构图:

3_class_data_bits_t结构.png

补充

1.从这部分分析,我们发现class_rw_t中的成员属性、实例方法、协议和class_ro_t结构体的的成员属性、实例方法、协议是一样的,那么为什么需要两套结构体存储呢? 联合实际开发中,我们会为类增加extension和category,会改变其成员属性和方法。这个在后续在分析,objc4的项目在runtime下是不能通过以上方法获取。

2.protocol_ref_t 的定义相当于 protocol_t , 表示是一个protocol_t list,去首地址( protocol_ref_t)后就可以通过链表获取协议数组了。

typedef uintptr_t protocol_ref_t;  // protocol_t *, but unremapped

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;
	// 以下是方法
}
复制代码

Supongo que te gusta

Origin juejin.im/post/7077020481496285220
Recomendado
Clasificación