Isa analysis to metalClass

The ISA_MASK is defined by the system. Different architectures vary. I get x86_64 and emulator in objC4 source code: define ISA_MASK 0x00007ffffffffff8ULL arm64: define ISA_MASK 0x0000000ffffffff8ULL

Create a new LGPerson object instance p, and explore the ISA point relationship through LLDB dynamic debug: object – isaisaisa > class – isaisaisa > metaClass

Code:

@interface LGPerson : NSObject @end @implementation LGPerson @end int main(int argc, Const char * argv[]) {@autoreleasepool {// nonpointerIsa mask 0x00007ffFFFFFFff8 LGPerson *p = [LGPerson alloc]; NSLog(@"%@",p); // 0x0000000100008360 VS 0x0000000100008338 // LGPerson : > class ISA -> metaclass ISA -> Root metaclass ISA -> root metaclass ISA -> root metaclass ISA -> root metaclass ISA -> root metaclass ISA // root metaclass isa -> root metaclass isa // inheritance chain} return 0; }Copy the code

Breakpoint tuning attempts:

Debug printing:

(lldb) x p 0x1005b3870: 65 83 00 00 01 80 1d 01 00 00 00 00 00 00 00 00 e............... 0x1005b3880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ (lldb) p/x p (LGPerson *) $1 = 0x00000001005b3870 (lldb) x/4gx 0x00000001005b3870 0x1005b3870: 0x011d800100008365 0x0000000000000000 0x1005b3880: 0x0000000000000000 0x0000000000000000 (LLDB) 0x011D800100008365 Is isa Error for our object: '0x011d800100008365' is not a valid command. (lldb) p 0x011d800100008365 & 0x00007ffffffffff8 (long) $2 = 4295000928 (lldb) p/x 0x011d800100008365 & 0x00007ffffffffff8 (long) $3 = 0x0000000100008360 (lldb) po 0x0000000100008360 LGPerson (lldb) x/4gx 0x0000000100008360 0x100008360: 0x0000000100008338 0x00007fff808be008 0x100008370: 0x00007fff204afaa0 0x0000802c00000000 (lldb) po 0x0000000100008338 & 0x00007ffffffffff8 LGPerson (lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8 (long) $6 = 0x0000000100008338 (lldb) $6 ! = $3Copy the code

Conclusion:

  1. p/x pprintLGPersonInstance object p ofMemory address
  2. x/4gx 0x00000001005b3870Formatted print0x00000001005b3870The data stored in the continuous address space under the address, got itLGPerson objecttheThe first address, which is the isa pointer address
  3. p/x 0x011d800100008365 & 0x00007ffffffffff8It is resolved by the and operation of the ISA pointer and ISA_MASKLGPerson class objecttheThe first address
  4. po 0x0000000100008360Print the address data, and you get itLGPerson
  5. x/4gx 0x0000000100008360Through the isa of instance object pointing to class object, I get the memory address of class object0x0000000100008360, format outputClass objectMemory address of
  6. p/x 0x0000000100008338 & 0x00007ffffffffff8The first address of the class object (isa pointer address)0x0000000100008338andISA_MASKDo and operation, get the memory address of metaclass ·0x0000000100008338
  7. 0x0000000100008360 VS 0x0000000100008338The addresses are different, but the prints are bothLGPerson, it isClass addresswithThe metaclass addressThe difference between
  8. LGPerson: Guess that the class will open up infinite memory with our object more than one class

Verify that classes and metaclasses will open up unlimited memory

Add methods to the above code to declare multiple objects and test to see if the class address changes

Code:

Class class1 = [LGPerson Class]; void lgTestClassNum(void){Class class1 = [LGPerson Class]; Class class2 = [LGPerson alloc].class; Class class3 = object_getClass([LGPerson alloc]); Class class4 = [LGPerson alloc].class; NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4); } int main(int argc, Const char * argv[]) {@autoreleasepool {// nonpointerIsa mask 0x00007ffFFFFFFff8 LGPerson *p = [LGPerson alloc]; NSLog(@"%@",p); // 0x0000000100008360 VS 0x0000000100008338 // LGPerson : There is more than one class // 0x0000000100008338 is not a class it isa metaclass // new thing // isa walk // object isa -> class isa -> metaclass isa -> root metaclass isa -> // root metaclass isa -> root metaclass isa // inheritance chain} return 0; }Copy the code

LLDB debugging:

2021-06-23 16:52:21.495202+0800 002-ISA analysis [12154:882912] <LGPerson: 0x1005B3870 > 2021-06-23 16:52:21.495968+0800 002- ISA Analysis [12154:882912] 0x100008360-0x100008360-0x100008360-0x100008360Copy the code

Conclusion:

LGPersontheClass objectOnly one copy in memory, not the same as the object!

Rotten Apple analyzes class and metaclass memory

Further use MatchOview rotten apple analysis 002-ISA analysis. Exec

Conclusion:

The LGPersonClass objectwithThe metaclassinexecOnly one of them

MetaClass -> rootMetaClass

From the previous section, we can see that isa for a class isa metaclass, and isa for a metaclass is the root metaclass

Debug printing:

(lldb) x/4gx p
0x10064e270: 0x011d800100008365 0x0000000000000000
0x10064e280: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x011d800100008365 & 0x00007ffffffffff8
(long) $1 = 0x0000000100008360
(lldb) x/4gx 0x0000000100008360
0x100008360: 0x0000000100008338 0x00007fff808be008
0x100008370: 0x00007fff204afaa0 0x0000802c00000000
(lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8
(long) $2 = 0x0000000100008338
(lldb) x/4gx 0x0000000100008338
0x100008338: 0x00007fff808bdfe0 0x00007fff808bdfe0
0x100008348: 0x00007fff204afaa0 0x0000e03500000000
(lldb) p/x 0x00007fff808bdfe0 & 0x00007ffffffffff8
(long) $3 = 0x00007fff808bdfe0
(lldb) po 0x00007fff808bdfe0
NSObject
(lldb) 
Copy the code

The attempt to:

Conclusion:

  1. x/4gx 0x0000000100008338throughClass objecttheIsa pointerTo find theMetaclass object addressFormat output metaclass object address, getYuan class objectIs the isa pointer0x0000000100008338
  2. p/x 0x0000000100008338 & 0x00007ffffffffff8Again, the isa pointer address and the ISA_MASK are interacted to get an address0x00007fff808bdfe0
  3. po 0x00007fff808bdfe0Output address0x00007fff808bdfe0, prints an NSObject.
  4. Root metaclass (rootMetaClass) is the NSObject

The root metaclass of NSObject is

(lldb) p/x NSObject.class
(Class) $5 = 0x00007fff808be008 NSObject
(lldb) x/4gx 0x00007fff808be008
0x7fff808be008: 0x00007fff808bdfe0 0x0000000000000000
0x7fff808be018: 0x0000000100648050 0x0001801000000003
(lldb) p/x 0x00007fff808bdfe0 & 0x00007ffffffffff8
(long) $6 = 0x00007fff808bdfe0
(lldb) po 0x00007fff808bdfe0
NSObject
Copy the code

Inheritance chain

Verify superClass and ISA for custom classes and subclasses

Create a new LGTeacher class that inherits from LGPerson, and verify that the class inherits as shown in the figure:

  1. Ttf_subclass classtheA metaclasswithSuperClass typetheA metaclassConsensus isNSObject class
  2. Ttf_subclass classtheThe parent classisSuperClass typeAnd theSuperClass typetheThe parent classisNSObject class(superClass inherits from NSObject)
  3. NSObject classIs the parent classnil, butNSObject classtheThe metaclassisNSObject metaclass; whileNSObject metaclasstheThe parent classisNSObject class, whose root metaclass is itself!

Code:

@interface LGPerson : NSObject @end @implementation LGPerson @end @interface LGTeacher : LGPerson @end @implementation LGTeacher @end void lgTestNSObject(void){// NSObject instance object NSObject *object1 = [NSObject alloc]; // NSObject Class Class = object_getClass(object1); // NSObject Class metaClass = object_getClass(Class); // NSObject Class rootMetaClass = object_getClass(metaClass); NSObject Class rootRootMetaClass = object_getClass(rootMetaClass); NSLog (@ "\ n \ n % p % p instance objects class p metaClass \ n \ n % % p root metaClass \ n % p spikes metaClass", object1, class, metaClass, rootMetaClass, rootRootMetaClass); Class pMetaClass = object_getClass(lgperson.class); Class psuperClass = class_getSuperclass(pMetaClass); NSLog(@"LGPerson metaclass parent: %@ - %p",psuperClass,psuperClass); Class tMetaClass = object_getClass(lgteacher.class); Class tsuperClass = class_getSuperclass(tMetaClass); NSLog(@"LGTeacher metaclass parent: %@ - %p",tsuperClass,tsuperClass); // NSObject root Class special case Class nsuperClass = class_getSuperclass(nsobject.class); NSLog(@"NSObject metaclass superclass: %@ - %p",nsuperClass,nsuperClass); -> NSObject Class rnsuperClass = class_getSuperclass(metaClass); NSLog (@ "NSObject root metaclass parent class: % @ - % p", rnsuperClass, rnsuperClass); } int main(int argc, const char * argv[]) { @autoreleasepool { // 0x00007ffffffffff8 LGPerson *p = [LGPerson alloc]; NSLog(@"%@",p); // 0x0000000100008360 VS 0x0000000100008338 // LGPerson : There is more than one class // 0x0000000100008338 is not a class it isa metaclass // new thing // isa walk // object isa -> class isa -> metaclass isa -> root metaclass isa -> Root metaclass isa -> root metaclass isa // lgTestNSObject(); } return 0; }Copy the code
2021-06-25 23:07:44.993380+0800 002- ISA Analysis [33538:2348495] 0x1007042A0 Instance object 0x7FFF808Be008 Class 0x7FFF808BDFe0 Metaclath 2021-06-25 23:07:44.994396+0800 002- ISA analysis of the parent class of the metaclass [33538:2348495] LGPerson: NSObject - 0x7FFF808BDFe0 2021-06-25 23:07:44.994518+0800 002- ISA analysis of the parent class of LGTeacher [33538:2348495] : LGPerson - 0x100008338 2021-06-25 23:07:44.994579+0800 002- Isa analysis of the metaclass parent of [33538:2348495] NSObject: (null) - 0x0 2021-06-25 23:07:44.994680+0800 002- ISA analysis [33538:2348495] NSObject Root metaclase parent: NSObject - 0x7FFF808Be008 (LLDB)Copy the code

Conclusion:

  1. NSOjectThe object’sMetaclasses and root metaclassesIt’s the same class
  2. Metaclasses also have inheritance relationships, just like classes, through printingLGTeachertheThe metaclassthesuperClasswithLGPersontheThe metaclassThey all got the same address0x100008338
  3. The parent class of the root metaclass points toNSObject, by printingNSObjectthesuperClassWith the NSObject class0x7fff808be008address
  4. The superclass of NSObject is (null) and the address is 0x0, which means that NSObject has no superclass

Class structure analysis

New and old versions of objc_class source code

The previous 1.0 version of objc_class

struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; #if ! __OBJC2__ Class _Nullable super_class OBJC2_UNAVAILABLE; const char * _Nonnull name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE; /* Use `Class` instead of `struct objc_class *` */Copy the code

New 2.0 objC_Class (based on objC4 (version 818) source code)

Class superclass holds the memory address of the parent Class. It takes 8 bytes of memory. Cache_t cache structure takes 16 bytes of memory. Class_data_bits_t bits manages structures that contain property, method, and so on. The memory size is determined by how many properties, methods, and so on are defined

struct objc_class : objc_object { objc_class(const objc_class&) = delete; objc_class(objc_class&&) = delete; void operator=(const objc_class&) = delete; void operator=(objc_class&&) = delete; // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; / / class_rw_t * plus custom rr/alloc flags / / below can be ignored... }Copy the code

Variable, array, structure, object memory similarities and differences

Follow up on the memory structure of class in the next section by comparing variables, arrays, structures, and objects with memory addresses and contents

Code:

#import <Foundation/Foundation.h> @interface LGPerson @end @implementation LGPerson @end #ifdef DEBUG #define LGNSLog(format, ...) Printf ("KC print: %s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]); #else #define LGNSLog(format, ...) ; #endif int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... // Int a = 10; // int b = 10; // LGNSLog(@"%d -- %p",a,&a); LGNSLog(@"%d -- %p",b,&b); // Object -lgPerson *p1 = [LGPerson alloc]; LGPerson *p2 = [LGPerson alloc]; LGNSLog(@"%@ -- %p",p1,&p1); LGNSLog(@"%@ -- %p",p2,&p2); Int c[4] = {1,2,3,4}; int *d = c; NSLog(@"%p - %p - %p",&c,&c[0],&c[1]); NSLog(@"%p - %p - %p",d,d+1,d+2); for (int i = 0; i<4; i++) { int value = *(d+i); NSLog(@"%d",value); } // OC class structure first address - shift some size -> content // lgperson. class address - shift all values NSLog(@" pointer - memory offset "); } return 0; }Copy the code

Debug printing:

KC Print: 10 -- 0x7ffeefBFF36c KC Print: 10 -- 0x7FFeEFBFF368 KC Print: <LGPerson: 0x105C040C0 > -- 0x7ffeEFBFF360 KC Print: <LGPerson: 0x105C040c0 > 0x105C040D0 > -- 0x7FFeEFBFF358 2021-06-24 16:44:03.478075+0800 002- Memory offset [23027:1912018] 0x7FFeEFBFF380-0x7FFeEFBFF380 - 0x7FFeEFBFF384 2021-06-24 16:44:03.478816+0800 002- Memory offset [23027:1912018] 0x7FFeEFB380-0x7FFeEFBFF384-0x7FfeEFBFF388 2021-06-24 16:50:34.623442+0800 002- Memory offset [23027:1912018] 1 2021-06-24 16:50:34.623577+0800 002- Memory offset [23027:1912018] 2 2021-06-24 16:50:34.623656+0800 002- Memory offset [23027:1912018] 3 2021-06-24 16:50:34.623712+0800 002- Memory offset [23027:1912018] 4 2021-06-24 16:50:34.624118+0800 002- Memory offset [23027:1912018] Pointer - Memory offset Program ended with exit code: 0Copy the code

Conclusion:

Variable: value copy object: Pointer address and content address are different Array: first address as array addressCopy the code

Class memory structure

By printing the memory contents of the class, look for the class_data_bits_t structure to learn the next properties and methods, ISA

Wrong idea

Debug breakpoints by building projects using OJC4.8! Incorrect preliminary inference about class memory distribution

Misunderstanding diagram:

Conclusion: The error is that the contents of the cache_t structure are not specifically analyzed

The right idea

Look specifically at the size of the cache_t structuresCopy the code

Methods in cache_t structures are not in memory. Static variables are in the global area and are not in their memory footprint

Private: explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //typedef unsigned long uintptr_t; Union {struct {explicit_atomic<mask_t> _maybeMask; union {struct {explicit_atomic<mask_t> _maybeMask; //typedef uint32_t mask_t; #if __LP64__ uint16_t_flags; // 1 byte #endif uint16_t _occupied; / / 2 bytes}; explicit_atomic<preopt_cache_t *> _originalPreoptCache; // The structure pointer only takes 8 bytes}; // The shared body takes just 8 bytes..... // Omit methods and staic variables because they do not occupy the memory that cache_t exploits..... }// This structure takes exactly 8+8=16 bytesCopy the code

Actually findclass_data_bits_t

Cache_t takes 16 bytes, Class ISA takes 8 bytes, and Class SuperClass takes 8 bytes. So looking for class_data_bits_t is exactly the address of the class pointer +0x20

Breakpoint debugging:

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available. (lldb) x/4gx LGPerson.class 0x100008380: 0x00000001000083a8 0x000000010036a140 0x100008390: 0x0000000100664f10 0x0002802800000003 (lldb) po 0x000000010036a140 NSObject (lldb) p/x NSObject.class (Class) $2 = 0x000000010036a140 NSObject (lldb) 0x0002802800000003 -> bit -> data error: '0x0002802800000003' is not a valid command. (lldb) x/5gx LGPerson.class 0x100008380: 0x00000001000083a8 0x000000010036a140 0x100008390: 0x0000000100664f10 0x0002802800000003 0x1000083a0: 0x0000000100664ed4 (lldb) x/6gx LGPerson.class 0x100008380: 0x00000001000083a8 0x000000010036a140 0x100008390: 0x0000000100664f10 0x0002802800000003 0x1000083a0: 0x0000000100664ed4 0x000000010036A0F0 (LLDB) Po sizeof(lgPerson. class) 8 (LLDB superclass(8bytes) + cache(16bytes) = 32 error: 'Is not a valid Command. (LLDB) Po 0x100008380+0x20 4295000992 (LLDB) p/x 0x100008380+0x20 (long) $7 = 0x00000001000083a0 (lldb) p (class_data_bits_t *)0x00000001000083a0 (class_data_bits_t *) $8 = 0x00000001000083a0 (lldb) p *$8 (class_data_bits_t) $9 = (bits = 4301672148) (lldb) p $8->data() (class_rw_t *) $10  = 0x0000000100664ed0 (lldb) p *$10 (class_rw_t) $11 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = nil nextSiblingClass = NSUUID }Copy the code

Conclusion:

  1. 0x000000010036a140LGPersonthesuperClass
  2. 0x100008380+0x20It’s the address of the LGPerson class plus 16 bytes offset, which happens to beclass_data_bits_t

Analysis of theclass_data_bits_tThe structure of the body

class_rw_tThe structure of the body

By analyzing the contents of class_data_bits_t, we can find that its main data content is the pointer to the structure that outputs class_rw_t

The source code:

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; Class_rw_t * data() const {return (class_rw_t *)(bits & FAST_DATA_MASK); } // Some code is omitted hereCopy the code

Conclusion:

By getting data() of type class_rw_t*

The source code:

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; Explicit_atomic <uintptr_t> ro_or_rw_ext; Class firstSubclass; Class nextSiblingClass; class_rw_ext_t *deepCopy(const class_ro_t *ro) { return extAlloc(ro, true); } 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

Conclusion:

By parsing the class_rw_T structure, you can retrieve information about the class, such as firstSubclass, methods, properties, protocols, deepCopy, ro, and so on

To obtainclass_rw_tthefirstSubclass

Class -> class_data_bits_t -> class_rw_t -> firstSubclass

Create a new LGPerson and LGTeacher object in the project, and set a breakpoint on LGTeacher. The first breakpoint only instantiates LGPerson, and the second run instantiates LGTeacher to verify the change in firstSubclass.

Breakpoint debugging

(lldb) p $1->data() (class_rw_t *) $2 = 0x000000010070d790 (lldb) p *$2 (class_rw_t) $3 = { flags = 2148007936 witness =  1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = nil nextSiblingClass = NSUUID} // enter the second break point 2021-06-25 01:02:38.456836+0800 KCObjcBuild[25790:2082491] 0x100604d60> (lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x0000000100726800 0x0002802800000003 (lldb) p *$2 (class_rw_t) $5 = { flags = 2148007936 witness = 1 ro_or_rw_ext = { std::__1::atomic<unsigned long> = { Value = 4295000344 } } firstSubclass = LGTeacher nextSiblingClass = NSUUID } (lldb)Copy the code

Conclusion:

Before subclass LGTeacher has no instance, firstSubclass in class_rw_t of LGPerson is nil, which side proves that object-c is runtime. P lgteacher. class can also be changed in LLDB, because OC class is lazy loaded!

To obtainclass_rw_tthepropertiesAn array of

Class -> class_data_bits_t -> class_rw_t -> property_array_t -> property_list_t -> property_t

Code:

#import <Foundation/Foundation.h> @interface LGPerson : NSObject{ NSString *subject; } @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *hobby; - (void)sayNB; + (void)say666; // 2: @end @implementation LGPerson - (instanceType)init{if (self = [super init]) {self.name = @"Cooci"; } return self; } - (void)sayNB{ } + (void)say666{ } @end @interface LGTeacher : LGPerson @property (nonatomic, copy) NSString *hobby; - (void)teacherSay; @end @implementation LGTeacher - (instanceType)init{if (self == [super init]) {NSLog(@" I'm here: %@",self); return self; } return nil; } - (void)teacherSay{ NSLog(@"%s",__func__); } @end int main(int argc, Const char * argv[] {@autoreleasepool {// LGPerson *p1 = [[LGPerson alloc] init]; LGTeacher *p2 = [[LGTeacher alloc] init]; // class_data_bits_t } return 0; }Copy the code

Breakpoint debugging:

(lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x0000000101907be0 0x0002802800000003 (lldb) p/x 0x100008388 + 0x20 (long) $1 = 0x00000001000083a8 (lldb) p (class_data_bits_t *)0x00000001000083a8 (class_data_bits_t *) $2 = 0x00000001000083a8 (lldb) p $2->data() (class_rw_t *)  $3 = 0x00000001019080a0 (lldb) p $3->properties() (const property_array_t) $4 = { list_array_tt<property_t, property_list_t, RawPtr> = { = { list = { ptr = 0x0000000100008260 } arrayAndFlag = 4295000672 } } } (lldb) p $4.list (const RawPtr<property_list_t>) $5 = { ptr = 0x0000000100008260 } (lldb) p $5.ptr (property_list_t *const) $6 = 0x0000000100008260 (lldb) p *$6 (property_list_t) $7 = { entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2) } (lldb) p $7.get(0) (property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name") (lldb) p $7.get(1) (property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")Copy the code

Steps:

  1. x/4gx LGPerson.classFormatted outputLGPerson.classTo get the first address of the class0x100008388
  2. p/x 0x100008388 + 0x20Offset the first address by 32 bytes (isa8 bytes,superclass8 bytes,cache16 bytes) to get the address of the class object attribute0x00000001000083a8
  3. p (class_data_bits_t *)0x00000001000083a8Convert the address toclass_data_bits_tType, for useclass_data_bits_tThe function of
  4. p $2->data()useclass_data_bits_tthedata()Function, getclass_rw_tAddress of type0x00000001019080a0
  5. p $3->properties()throughproperties()Function to obtainLGPersonThe list of member variables
  6. p $4.listandp $5.ptrResolve theproperty_list_tThe address of the
  7. p *$6Get a member variable by taking its addressproperty_list_t
  8. p $7.get(0)withp $7.get(1)Gets the member variables of a class individually through a c++ functionname,hobby

To obtainclass_rw_tthemethodsAn array of

Nsobject. class -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t -> big

Class_rw_t (); class_rw_t (); class_rw_t (); class_rw_t (); There is also a layer of big() in the method_t structure, so you have to parse one more layer of structure to get the instance method. But the process found that there is no class method! So this is an instance method

The source code:

struct method_t { static const uint32_t smallMethodListFlag = 0x80000000; method_t(const method_t &other) = delete; // 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; }; // Omitted part of the source codeCopy the code

Comparison between method and Propetry:

Breakpoint debugging:

(lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x0000000101304120 0x0002802800000003 (lldb) p/x 0x100008388 + 0x20 (long) $1 = 0x00000001000083a8 (lldb) p (class_data_bits_t *)0x00000001000083a8 (class_data_bits_t *) $2 = 0x00000001000083a8 (lldb) p $2->data() (class_rw_t *)  $3 = 0x00000001013040a0 (lldb) p $3->methods() (const method_array_t) $4 = { list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = { = { list = { ptr = 0x0000000100008160 } arrayAndFlag = 4295000416 } } } (lldb) p $4.list (const method_list_t_authed_ptr<method_list_t>) $5 = { ptr = 0x0000000100008160 } (lldb) p $5.ptr (method_list_t *const)  $6 = 0x0000000100008160 (lldb) p *$6 (method_list_t) $7 = { entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6) } (lldb) p $7.get(0).big() (method_t::big) $8 = { name = "sayNB" types = 0x0000000100003f77 "v16@0:8" imp = 0x0000000100003d40 (KCObjcBuild`-[LGPerson sayNB]) } (lldb) p $7.get(1).big() (method_t::big) $9 = { name = "hobby" types = 0x0000000100003f6f "@16@0:8" imp = 0x0000000100003db0 (KCObjcBuild`-[LGPerson hobby]) } (lldb) p $7.get(2).big()  (method_t::big) $10 = { name = "setHobby:" types = 0x0000000100003f8b "v24@0:8@16" imp = 0x0000000100003de0 (KCObjcBuild`-[LGPerson setHobby:]) } (lldb) p $7.get(3).big() (method_t::big) $11 = { name = "init" types = 0x0000000100003f6f "@16@0:8" imp = 0x0000000100003ce0 (KCObjcBuild`-[LGPerson init]) } (lldb) p $7.get(4).big() (method_t::big) $12 = { name = "name" types = 0x0000000100003f6f "@16@0:8" imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson name]) } (lldb) p $7.get(5).big() (method_t::big) $13 = { name = "setName:" types = 0x0000000100003f8b "v24@0:8@16" imp = 0x0000000100003d80 (KCObjcBuild`-[LGPerson setName:]) }Copy the code

Steps:

  1. x/4gx LGPerson.classFormatted outputLGPerson.classTo get the first address of the class0x100008388
  2. p/x 0x100008388 + 0x20Offset the first address by 32 bytes (ISA8 bytes, Superclass8 bytes, cache16 bytes) to get the class object property address0x00000001000083a8
  3. p (class_data_bits_t *)0x00000001000083a8Convert the address toclass_data_bits_tType, for useclass_data_bits_tThe function of
  4. p $2->data()useclass_data_bits_tthedata()Function, getclass_rw_tAddress of type0x00000001013040a0
  5. p $3->methods()throughmethods()Function to obtainLGPersonThe list of instance methodsmethod_array_t
  6. p $4.listandp $5.ptrResolve themethod_list_tThe address of the
  7. p *$6Get an array of instance variables by taking an addressmethod_list_t. entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6), there are obviously six instance methods
  8. Through c++ functionsget()withbig()To obtain a single instance of a class:

    p $7.get(0).big():-[LGPerson sayNB] Custom instance methodsayNB

    p $7.get(1).big():-[LGPerson hobby] Member variablehobbythegetterMethods are generated by the system

    p $7.get(2).big():-[LGPerson init] Custominitmethods- (instancetype)init

    p $7.get(3).big():-[LGPerson name] Member variablenameThe getter method for is generated by the system

    p $7.get(0).big():-[LGPerson setName:] Member variablenameThe setter method for psi is generated by the system

Get ivars (member variables) for class_rw_t

Nsobject. class -> class_data_bits_t -> class_rw_t -> class_ro_t -> iVAR_list_t -> iVAR_t

The source code:

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; // With ptrauth, this is signed if it points to a small list, but // may be unsigned if it points to a big list. void *baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; // Omitted part of the code}Copy the code
struct ivar_t { #if __x86_64__ // *offset was originally 64-bit on some x86_64 platforms. // We read and write only 32 bits of it. // Some metadata provides all 64 bits. This is harmless for unsigned // little-endian values. // Some code uses all 64 bits. class_addIvar() over-allocates the // offset for their benefit. #endif int32_t *offset; const char *name; const char *type; // alignment is sometimes -1; use alignment() instead uint32_t alignment_raw; uint32_t size; uint32_t alignment() const { if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT; return 1 << alignment_raw; }};Copy the code

LLDB dynamic debugging

(lldb) x/4gx LGPerson.class 0x100008388: 0x00000001000083b0 0x000000010036a140 0x100008398: 0x000000010135c630 0x0002802800000003 (lldb) p/x 0x100008388 + 0x20 (long) $1 = 0x00000001000083a8 (lldb) p (class_data_bits_t *)$1 (class_data_bits_t *) $2 = 0x00000001000083a8 (lldb) p $2->data() (class_rw_t *) $3 = 0x000000010135c5f0 (lldb) p $3->ro() (const class_ro_t *) $4 = 0x0000000100008118 (lldb) p *$4 (const class_ro_t) $5 = {  flags = 0 instanceStart = 8 instanceSize = 32 reserved = 0 = { ivarLayout = 0x0000000000000000 nonMetaclass = nil } name = { std::__1::atomic<const char *> = "LGPerson" { Value = 0x0000000100003ea8 "LGPerson" } } baseMethodList = 0x0000000100008160 baseProtocols = 0x0000000000000000 ivars = 0x00000001000081f8 weakIvarLayout = 0x0000000000000000 baseProperties = 0x0000000100008260 _swiftMetadataInitializer_NEVER_USE = {} } (lldb) p $5.ivars (const ivar_list_t *const) $6 = 0x00000001000081f8 (lldb) p *$6 (const ivar_list_t) $7 = { entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3) } (lldb) p $7.get(0) (ivar_t) $8 = { offset = 0x0000000100008320 name = 0x0000000100003f2f "subject" type = 0x0000000100003f7f "@\"NSString\"" alignment_raw = 3 size = 8 } (lldb) p $7.get(1) (ivar_t) $9 = { offset = 0x0000000100008328 name = 0x0000000100003f37 "_name" type = 0x0000000100003f7f "@\"NSString\"" alignment_raw = 3 size = 8 } (lldb) p $7.get(2) (ivar_t) $10 = { offset = 0x0000000100008330 name = 0x0000000100003f3d "_hobby" type = 0x0000000100003f7f "@\"NSString\"" alignment_raw = 3 size = 8 }Copy the code

steps

  1. x/4gx LGPerson.classFormatted outputLGPerson.classTo obtain the initial address0x100008388
  2. p/x 0x100008388 + 0x20First address offset 32 bytes (isa8 bytes,superclass8 bytes,cache_t16 bytes) to get the object that contains the class attribute method member variablesclass_data_bits_tThe address of the0x00000001000083a8
  3. p (class_data_bits_t *)$1Translates the address toclass_data_bits_t, in order to useclass_data_bits_tThe function of
  4. p $2->data()useclass_data_bits_tthedata()Function, getclass_rw_tAddress of type0x000000010135c5f0
  5. p $3->rouseclass_rw_ttheroFunction, getclass_ro_tAddress of type0x0000000100008118
  6. p *$4lass_ro_tType the address0x0000000100008118I got itclass_ro_tobject
  7. p $5.ivarsuseivarsFunction to obtainclass_ro_tThe object’sivarsI get the pointivar_list_taddress0x00000001000081f8A pointer to the
  8. p *$6By taking the address0x00000001000081f8To get an array of instance variablesivar_list_t.entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)You can see that there are three ivars
  9. Through c++ functionsget()To obtain a single instance of a class:

User declared member variable subject p $7.get(0): (ivar_t) $8 = { offset = 0x0000000100008320 name = 0x0000000100003f2f “subject” type = 0x0000000100003f7f “@”NSString”” alignment_raw = 3 size = 8 }

_name p $7.get(1): (ivar_t) $9 = { offset = 0x0000000100008328 name = 0x0000000100003f37 “_name” type = 0x0000000100003f7f “@”NSString”” alignment_raw = 3 size = 8 }

_hobby p $7. Get (2): (ivar_t) $10 = { offset = 0x0000000100008330 name = 0x0000000100003f3d “_hobby” type = 0x0000000100003f7f “@”NSString”” alignment_raw = 3 size = 8 }

Conclusion: IVars exists in ro, and the member variable automatically generates attributes _name,_hobby

Gets the class method for class_rw_t

We have obtained the method list of class and found that there is no class method, so the methods obtained by class class directly are instance methods, and the class methods have nowhere to go? That type of method has to be in the metaclass metalClass.

Class method acquisition process: NSObject.class -> metaClass -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t -> big

Breakpoint debugging print:

(lldb) x/4gx LGPerson.class 0x100008390: 0x00000001000083b8 0x000000010036a140 0x1000083a0: 0x0000000101470ab0 0x0002802800000003 (lldb) p/x 0x00000001000083b8 & 0x00007ffffffffff8 (long) $1 = 0x00000001000083b8 (lldb) x/4gx 0x00000001000083b8 0x1000083b8: 0x000000010036a0f0 0x000000010036a0f0 0x1000083c8: 0x0000000100731720 0x0001e03100000007 (lldb) p/x 0x1000083b8 + 0x20 (long) $2 = 0x00000001000083d8 (lldb) p (class_data_bits_t *)$2 (class_data_bits_t *) $3 = 0x00000001000083d8 (lldb) p $3->data() (class_rw_t *) $4 = 0x0000000101470a50 (lldb) p $4->methods() (const method_array_t) $5 = { list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = { = { list = { ptr = 0x00000001000082d0 } arrayAndFlag = 4295000784 } } } (lldb) p $5.list (const method_list_t_authed_ptr<method_list_t>) $6 = { ptr = 0x00000001000082d0 } (lldb) p $6.ptr (method_list_t *const)  $7 = 0x00000001000082d0 (lldb) p *$7 (method_list_t) $8 = { entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1) } (lldb) p $8.get(0).big() (method_t::big) $9 = { name = "say666" types = 0x0000000100003f77 "v16@0:8" imp = 0x0000000100003e10 (KCObjcBuild`+[LGPerson say666]) } (lldb)Copy the code

Steps:

  1. x/4gx LGPerson.classFormat print classLGPersonTo get the first address of the class0x100008390
  2. p/x 0x00000001000083b8 & 0x00007ffffffffff8willIsa pointerandISA_MASKdowithOperation, getLGPersonThe metaclassmetaClass
  3. x/4gx 0x00000001000083b8, format and printLGPersonthemetaClassTo get the first address of the metaclass0x1000083b8
  4. p/x 0x1000083b8 + 0x20, offset the metaclass’s first address by 32 bytes (ISA8 bytes,superclass8 bytes,cache_t16 bytes) of the multivariate classclass_data_bits_tAddress of the object0x00000001000083d8
  5. p (class_data_bits_t *)0x00000001000083d8Convert the address toclass_data_bits_tObject for calling functions
  6. p $3->data()callclass_data_bits_tdataFunction, getclass_rw_tAddress of the object0x0000000101470a50
  7. p $4->methods()To obtainclass_rw_tthemethodsMethods list
  8. p $5.listandp $5.ptrTo get the pointmethod_list_tPointer to address0x00000001000082d0
  9. p *$7Get the address. Got itmethod_list_tObject, count is 1,entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1), has a class method
  10. Through c++ functionsget()withbig()Class method for obtaining a single class:
  11. p $7.get(0).big():

(method_t::big) $9 = {

name = “say666

types = 0x0000000100003f77 “v16@0:8”

imp = 0x0000000100003e10 (KCObjcBuild+[LGPerson say666])

}