- IOS underlying exploration of alloc
- IOS: Isa and superClass
Isa refers to class information, metaclass information, and supperClass information. Isa refers to class information and metaclass information.
Class object basic data structure
- The corresponding structure of Class is objC_class, which has only four member variables: ISA, superClass, cache, and bits. Information about Class objects is stored in bits.
struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // These are all methods}Copy the code
- The class_datA_bits_t structure, we know that static data is stored in the global area, not in the structure. So class_datA_bits_t has just two attributes, _bucketsAndMaybeMask and union, which add up to 16 bytes. A. Uintptr_t is an unsigned long of 8 bytes, so _bucketsAndMaybeMask is 8 bytes. Uint32_t = uint32_t = 4 bytes; uint16_t = 2 bytes; in 64-bit environment, the struct of union is 8 bytes. So the length of the union is 8 bytes.
typedef unsigned long uintptr_t; // 4 bytes #if __LP64__ typedef uint32_t; // x86_64&arm64 asm are less efficient with 16-bits 4 bytes #else typedef uint16_t mask_t; // 2 bytes #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}Copy the code
3. Class_data_bits_t is a struct of 8 bytes. Bits data is mainly stored in data() and safe_ro(). Data () stores data such as class member variables, protocols, methods, and attributes.
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; }}}Copy the code
4. Class_rw_t data() structure
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}; }}}Copy the code
Translation memory
We can first obtain the class object, and then obtain the memory address of class_datA_bits_t bits by memory translation, and then further obtain the relevant information of the class.
Get the class_rw_t structure
Struct class_rw_t holds class member variables, attributes, methods, protocols, etc. 1. Obtain the memory address of the LKXPerson object
(lldb) p/x LKXPerson.class
(Class) $0 = 0x00000001000084a0
Copy the code
2. Obtain class_DATA_bits_t bits and know that there are three variables in front of bits with 32 bytes. Therefore, 0x0000000100008368 + 0x20 = 0x0000000100008388 and convert the bits
(lldb) p/x 0x00000001000084a0 + 0x20
(long) $1 = 0x00000001000084c0
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000084c0
Copy the code
3. Obtain struct class_rw_t
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100a17490
Copy the code
4. 3 variables are used to store the class_rw_T structure, and we can store the class_rw_T structure with 3 variables. We can now use 3(class_rw_t) to get the member variables, attributes, methods, and protocols of the class. ` `
The class information class_ro_t
The class_ro_t structure is immutable, as its name indicates. It contains member variables, member attributes, instance methods, and protocols that are declared and implemented by the object.
- Get the pointer to the class_ro_t structure by ro() of $4(class_rw_t) :
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x00000001000081b0
Copy the code
- The class_ro_t structure is defined as follows:
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;
}
Copy the code
- Print class_ro_t data:
(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 = {}
}
Copy the code
Member variables
- We can use ivars to get the memory address of a member variable:
Ivars (const ivar_list_t *const) $6 = 0x00000001000082d8 // The first address where ivars stores data (LLDB) p *$6 (const ivar_list_t) $7 = { entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4) }Copy the code
- Ivars ends up with a linked list of length 4, which means there are four member variables. We print the member variables:
(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 }Copy the code
- We analyze member variables and find that defined member attributes automatically generate member variables for them
baseProtocols
- The pointer to the protocol linked list was obtained
(lldb) p $5->baseProtocols
(protocol_list_t *const) $12 = 0x0000000100008188
Fix-it applied, fixed expression was:
$5.baseProtocols
Copy the code
- Get memory address of protocol linked list:
(lldb) p *$12
(protocol_list_t) $13 = (count = 3, list = protocol_ref_t [] @ 0x00006000027af638)
Copy the code
- Protocol_list_t is defined in objC4, I intelligently get the address of its first protocol.
// define struct protocol_list_t {// count is pointer-sized by accident. Uintptr_t count; protocol_ref_t list[0]; }Copy the code
- Get protocol list pointer:
(lldb) p $13.list[0]
(protocol_ref_t) $14 = 4295001376
Copy the code
5. Obtain the first address of the protocol list:
(lldb) p (protocol_t *)$14
(protocol_t *) $15 = 0x0000000100008520
Copy the code
6. Get all agreements:
(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
}
Copy the code
- By analyzing “mangledName”, it follows the same protocol as LKXPerson.
baseProperties
- Gets the pointer address of a member property:
(lldb) p $5.baseProperties
(property_list_t *const) $18 = 0x0000000100008360
Copy the code
- Since struct property_list_t is a linked list, :
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
Copy the code
- So I try to get it item by 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.
Copy the code
- Getting the third member is an error, indicating that there are as many free member variables as we defined.
baseMethods
- Gets a pointer to the method list
(lldb) p $5.baseMethods
(const WrappedPtr<method_list_t, method_list_t::Ptrauth>) $24 = {
ptr = 0x00000001000081f8
}
Copy the code
- In get method list memory address:
(lldb) p $24.ptr
(method_list_t *const) $25 = 0x00000001000081f8
Copy the code
- Obtain method list details:
(lldb) p *$25
(method_list_t) $26 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 9)
}
Copy the code
Struct big contains the name of the method, parameters, return values, function addresses, etc., defined as follows:
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;
};
}
Copy the code
5. Get all the methods
(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) }Copy the code
6. The method list holds all instance methods, including private methods, getter and setter methods automatically generated for member properties, and a systematic C++ destructor.
Get information about metaclasses
- Obtain isa in metaclass memory address, the first 8 bytes of the class information store the address of the class information, print also LKXPerson, can confirm that it isa metaclass.
(lldb) x/4gx $0
0x1000084a0: 0x0000000100008478 0x0000000100802140
0x1000084b0: 0x0000000100a95820 0x0001803400000003
(lldb) po 0x0000000100008478
LKXPerson
Copy the code
- To obtain the metaclass objc_class’s memory address, match the metaclass memory address with & on 0x00007FFFFFFFF8
lldb) p/x 0x0000000100008478 & 0x00007ffffffffff8
(long) $37 = 0x0000000100008478
Copy the code
- Then move 20 bytes to get 0x0000000100008360, the class_datA_bits_t structure pointer address, which points to the memory address:
(lldb) p/x 0x0000000100008478 + 0x20
(long) $38 = 0x0000000100008498
(lldb) p (class_data_bits_t *)$38
(class_data_bits_t *) $39 = 0x00000001000084a8
Copy the code
- Get the internal address of class_rw_t:
(lldb) p $39->data()
(class_rw_t *) $40 = 0x0000000100a28760
Copy the code
- Gets the class_ro_T structure for the class information
(lldb) p $40->ro()
(const class_ro_t *) $42 = 0x00000001000080d8
Copy the code
- Print class information class_ro_t structure:
(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 = {} }Copy the code
- We see that member variables, member properties, are nil, the list of protocols and methods has values.
- We get the protocol list, and we get three protocols, the same number we declared. In fact, the agreement to change the declaration we can find out that we made the declaration.
(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 }Copy the code
- Our class_ro_t structure gets the list of class methods as we declare them:
(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)
}
Copy the code
- Get class attribute null:
(lldb) p $40->properties()
(const property_array_t) $57 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = nil
}
arrayAndFlag = 0
}
}
}
Copy the code
- Get the same number of class methods as we declared earlier:
(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)
}
Copy the code
- The fetch class complies with the same three protocols as we declare:
(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)
Copy the code
- You can see that the class_ro_T structure is exactly the same as the protocols and methods in the class information, so you can be sure that the metaclass holds the class methods and protocols.
Get instance method
- Get a pointer to the instance method list:
(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
}
}
}
Copy the code
- Get the instance method pointer address:
(lldb) p $70.list.ptr
(method_list_t *const) $71 = 0x00000001000081f8
Copy the code
3. Obtain instance method structure stored address:
(lldb) p *$71
(method_list_t) $72 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 9)
}
Copy the code
4. There are 9 instance methods:
(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) }Copy the code
7. Analyze the methods here, including declared instance methods, private methods, getter and setter methods automatically generated by member properties, system C++ destructor methods.
agreement
- Gets the protocol list pointer address
(lldb) p $3->protocols()
(const protocol_array_t) $82 = {
list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008188
}
arrayAndFlag = 4295000456
}
}
}
Copy the code
- Get protocol list memory address:
(lldb) p $82.list.ptr
(protocol_list_t *const) $83 = 0x0000000100008188
Copy the code
- Gets the protocol list pointer
(lldb) p $83.list[0]
(protocol_ref_t) $85 = 4295001376
Fix-it applied, fixed expression was:
$83->list[0]
Copy the code
- Obtain the protocol header address:
(lldb) p (protocol_t *)$85
(protocol_t *) $90 = 0x0000000100008520
Copy the code
- Print protocol:
(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
}
Copy the code
- With the mangledName attribute of the protocol, we can get the same as the protocol we followed
Class attribute
- We add class attributes to LKXPerson:
@property (class, nonatomic, copy) NSString *clsProperty;
Copy the code
- Get class attributes:
(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)Copy the code
- You can see that the metaclass holds the class attributes, in baseProperties and Properties (), respectively.
conclusion
Through the analysis of struct class_RW_T, the following points are obtained:
- Struct class_rw_t directly holds the instance methods (method_array_t *), instance properties (property_array_t), and protocol (PROTOCOL_array_t) of the class, all of which are two-dimensional arrays.
- Struct class_rw_t const class_ro_t *ro() holds the class member variable (const ivar_list_t *), instance property (property_list_t) *), protocol compliance (protocol_list_t *), instance methods (WrappedPtr < method_list_t, method_list_t::Ptrauth >), but the memory here is immutable and both are one-dimensional arrays.
- The struct class_rw_t of the metaclass object directly holds the class methods (method_array_t *), protocol (PROTOCOL_array_t), and class properties (property_array_t) of the class, both of which are two-dimensional arrays.
- Struct class_rw_t = const class_ro_t *ro(); WrappedPtr < method_list_t; Method_list_t ::Ptrauth >), class properties (property_list_t *).
- Class structure diagram:
supplement
Class_ro_t: member attributes, instance methods, and protocols are the same as class_ro_t: member attributes, instance methods, and protocols. In joint development, we will add extension and category to the class and change its member attributes and methods. In the following analysis, objC4’s items in runtime cannot be obtained by the above method.
2. The protocol_ref_t is defined as a protocol_t, indicating a protocol_t list. The first address of the list is 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; // Here are the methods}Copy the code