对象在底层的结构是objc_object
,类在底层的结构是objc_class
,类结构中包括哪些信息。
类的内存结构
在objc4-818.2
的源码中,类的简要结构是这样的,包括isa
(继承自objc_object),superclass
,cache
,bits
。
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 memory
和dirty memory
.
clean memory
是指加载之后不回发生改变的内存。dirty memory
是指运行时会发生改变的内存。
dirty memory
要比clean memory
更昂贵,在存储的过程中,我们尽量把不会改变的保存在clean memory
中,把会改变的部分存储在dirty memory
。class_ro_t
属于clean memory
,它是read only,从磁盘加载到内存就是固定的数据。class_rw_t
是dirty memory
,从这里再分离出一个class_rw_ext_t
的结构,这部分是改变比较多的部分,放在dirty memory
中。
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
的大小。
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_t
是uint32_t
大小为4,uint16_t
大小为2,所以联合体的大小为8字节。那么cache_t
的大小为16字节。
- lldb计算
cache_t
的大小
类结构中,前两个为isa
和Class
,大小都是8字节,那么cache_t
就是从类的首地址向下偏移16个字节。再使用sizeof函数算出大小。
获取到cache_t
的大小,我们就可以取到class_data_bits_t
从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
,这是一个二维数组,结构图如下:
从class_data_bits_t
中取出方法列表
这是我们类的结构,看看方法列表中都有什么
(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
}
复制代码