上篇分别研究了isa和supperClass的指针指向内存内容,提到isa指向类信息、元类信息,这篇来看看类信息和元类信息中都存储哪些内容。
类对象基本数据结构体
- 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;
// 下面都是方法
}
复制代码
- 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(class_rw_t)来获取类的成员变量、属性、方法、协议。``
类信息class_ro_t
结构体class_ro_t从名称就可知,这部分是不可变的,这里保存着对象声明及实现的成员变量、成员属性、实例方法、协议。
- 通过$4(class_rw_t)的ro()获取class_ro_t结构体的指针:
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x00000001000081b0
复制代码
- 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;
}
复制代码
- 打印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 = {}
}
复制代码
成员变量
- 我们可通过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)
}
复制代码
- 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
}
复制代码
- 我们分析成员变量发现,定义的成员属性也会为其自动生成成员变量
baseProtocols
- 获取到协议链表的指针
(lldb) p $5->baseProtocols
(protocol_list_t *const) $12 = 0x0000000100008188
Fix-it applied, fixed expression was:
$5.baseProtocols
复制代码
- 获取协议链表的内存地址:
(lldb) p *$12
(protocol_list_t) $13 = (count = 3, list = protocol_ref_t [] @ 0x00006000027af638)
复制代码
- 得到其遵守了三个协议,和我们定义(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];
}
复制代码
- 获取协议列表指针:
(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
}
复制代码
- 通过分析“mangledName”,和LKXPerson遵守的协议是一样的。
baseProperties
- 获取成员属性的指针地址:
(lldb) p $5.baseProperties
(property_list_t *const) $18 = 0x0000000100008360
复制代码
- 因为struct property_list_t是一个链表,:
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
复制代码
- 所以我尝试通过一个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.
复制代码
- 获取第三个是就报错,说明自由三个成员变量,和我们定义的是一样多。
baseMethods
- 获取方法列表指针
(lldb) p $5.baseMethods
(const WrappedPtr<method_list_t, method_list_t::Ptrauth>) $24 = {
ptr = 0x00000001000081f8
}
复制代码
- 在获取方法列表内存地址:
(lldb) p $24.ptr
(method_list_t *const) $25 = 0x00000001000081f8
复制代码
- 获取方法列表详细信息:
(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++析构方法。
获取元类的信息
- 获取元类内存地址中的isa,类信息前8个字节保存类信息地址,打印也是LKXPerson,可以确认是元类。
(lldb) x/4gx $0
0x1000084a0: 0x0000000100008478 0x0000000100802140
0x1000084b0: 0x0000000100a95820 0x0001803400000003
(lldb) po 0x0000000100008478
LKXPerson
复制代码
- 将元类内存地址与&上 0x00007ffffffffff8 就可以得到元类objc_class的内存地址
lldb) p/x 0x0000000100008478 & 0x00007ffffffffff8
(long) $37 = 0x0000000100008478
复制代码
- 然后通过移动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
复制代码
- 获取class_rw_t结构体内存地址:
(lldb) p $39->data()
(class_rw_t *) $40 = 0x0000000100a28760
复制代码
- 获取类信息的class_ro_t结构体
(lldb) p $40->ro()
(const class_ro_t *) $42 = 0x00000001000080d8
复制代码
- 打印类信息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 = {}
}
复制代码
- 我们看到成员变量、成员属性、是nil,协议和方法列表有值。
- 我们获取协议列表信息,获取到由三个协议,和我们声明的个数一样的。其实改变申明的协议我们可以发现就是我们申明的。
(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
}
复制代码
- 我们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)
}
复制代码
- 获取类属性为空:
(lldb) p $40->properties()
(const property_array_t) $57 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = nil
}
arrayAndFlag = 0
}
}
}
复制代码
- 获取类方法,得到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)
}
复制代码
- 获取类遵守的协议是三个和我们申明的一样:
(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)
复制代码
- 可以看到class_ro_t结构体和类信息中的协议和方法一模一样,所以可以确定元类中保存着类方法和协议。
获取实例方法
- 获取实例方法列表指针:
(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
}
}
}
复制代码
- 获取实例方法指针地址:
(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++析构方法。
协议
- 获取协议列表指针地址
(lldb) p $3->protocols()
(const protocol_array_t) $82 = {
list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008188
}
arrayAndFlag = 4295000456
}
}
}
复制代码
- 获取协议列表内存地址:
(lldb) p $82.list.ptr
(protocol_list_t *const) $83 = 0x0000000100008188
复制代码
- 获取协议列表指针
(lldb) p $83.list[0]
(protocol_ref_t) $85 = 4295001376
Fix-it applied, fixed expression was:
$83->list[0]
复制代码
- 获取协议首地址:
(lldb) p (protocol_t *)$85
(protocol_t *) $90 = 0x0000000100008520
复制代码
- 打印协议:
(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
}
复制代码
- 通过协议的mangledName属性我们可以得到和遵守的协议时一样
类属性
- 我们为LKXPerson加上类属性:
@property (class, nonatomic, copy) NSString *clsProperty;
复制代码
- 获取类属性:
(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)
复制代码
- 可以看到元类中是保存类属性,分别在baseProperties和properties()中。
总结
通过对 struct class_rw_t 的分析,得出以下几点:
- 类对象对应的 struct class_rw_t 直接保存着类的实例方法(method_array_t *)、实例属性(property_array_t)、遵守的协议(protocol_array_t),这三个都是二维数组。
- 类的 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 > ),但是这里的内存是不可变的,而且都是一维数组。
- 元类对象对应的 struct class_rw_t 直接保存着类的类方法(method_array_t *)、遵守的协议(protocol_array_t)、类属性(property_array_t),这两个都是二维数组。
- 元类的 struct class_rw_t 获取 const class_ro_t *ro() 将得到里面保存着遵守的协议(protocol_list_t *)、类方法(WrappedPtr < method_list_t, method_list_t::Ptrauth > )、类属性(property_list_t *)。
- 类的结构图:
补充
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;
// 以下是方法
}
复制代码