preface

We have focused on objects in previous articles, but what is a class? So let’s go into the analysis.

Isa bit and class inheritance chain

1. Isa uses the bitmap

The metaclass

In the nature of objects article, we mentioned taking class information according to ISA and the mask (ISA_MASK) :

(lldb) x/4gx p1
0x1011422c0: 0x011d8001000081d1 0x0000000000000000
0x1011422d0: 0x697263534b575b2d 0x67617373654d7470
(lldb) po 0x011d8001000081d1 & 0x00007ffffffffff8ULL
WSPerson

(lldb) p/x 0x011d8001000081d1 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000081d0 
Copy the code

Now that we have the class information, let’s continue with x/4gx

(lldb) x/4gx 0x00000001000081d0
0x1000081d0: 0x00000001000081a8 0x0000000100357140
0x1000081e0: 0x000000010034f360 0x0000801000000000
Copy the code

What’s this? How is it worth it? What if, as above, we also use ISA_MASK bits and down? Give it a try:

(lldb) po 0x00000001000081a8 & 0x00007ffffffffff8ULL
WSPerson

(lldb) p/x 0x00000001000081a8 & 0x00007ffffffffff8ULL
(unsigned long long) $4 = 0x00000001000081a8 
Copy the code

This? 0x00000001000081d0 and 0x00000001000081a8 are both WSPerson. Multiple classes in memory? With a guess, let’s test it:

WSPerson *c1 = WSPerson.class;
WSPerson *c2 = [WSPerson alloc].class;
WSPerson *c3 = object_getClass([WSPerson alloc]);
WSPerson *c4 = [WSPerson alloc].class;
    
NSLog(@"\nc1: %p\nc2: %p\nc3: %p\nc4: %p", c1, c2, c3, c4);
/ / to get:
/* c1: 0x1000081d0 c2: 0x1000081d0 c3: 0x1000081d0 c4: 0x1000081d0 */
Copy the code

0x00000001000081d0 is a class and 0x00000001000081a8 is not.

  1. In the process ofProductsDirectory get executable (black)
  2. And then useMachOViewOpen the
  • First look at Section64(_DATA,__objc_classrefs), which records the address of the referenced class:



    • Can be determined0x00000001000081d0Is theWSPersonClass, and only one.
  • Then we find Symbols in the Symbol table and search for the keyword class.



    • First of all,_OBJC_CLASSisobjc_classWe know that, but_OBJC_METACLASSWhat is it? We didn’t create this, right?
    • _OBJC_METACLASScallThe metaclass“Is created by the system.

Isa walk a

  • In the above steps, we get(yuan)So let’s go ahead and find outThe metaclasstheisaTo:
(lldb) x/4gx 0x00000001000081a8
0x1000081a8: 0x00000001003570f0 0x00000001003570f0
0x1000081b8: 0x0000000101042d90 0x0001e03100000007
(lldb) p/x 0x00000001003570f0 & 0x00007ffffffffff8ULL
(unsigned long long) $5 = 0x00000001003570f0
(lldb) po 0x00000001003570f0
NSObject
Copy the code

Here we get NSObject, which is the root metaclass, showing the metaclass, and we look for isa points to the root metaclass:

(lldb) x/4gx 0x00000001003570f0
0x1003570f0: 0x00000001003570f0 0x0000000100357140
0x100357100: 0x00000001010434d0 0x0004e03100000007
(lldb) p/x 0x00000001003570f0 & 0x00007ffffffffff8ULL
(unsigned long long) $7 = 0x00000001003570f0
Copy the code

The root metaclass’s ISA points to itself.

  • Let’s seeNSObjecttheisaTo:
(lldb) p/x NSObject.class
(Class) $10 = 0x0000000100357140 NSObject
(lldb) x/4gx 0x0000000100357140
0x100357140: 0x00000001003570f0 0x0000000000000000
0x100357150: 0x000000010114a0a0 0x0002801000000003
(lldb) p/x 0x00000001003570f0 & 0x00007ffffffffff8ULL
(unsigned long long) $11 = 0x00000001003570f0
Copy the code

Here we find that the ISA of NSObject points to the root metaclass, and then the ISA of the root metaclass points to itself.

  • So we getisaWalk the bitmap:

2. The inheritance chain

We know that classes have inheritance relationships, but how about Meta? Let’s explore:

  • So let’s print it out firstNSObjectRelated information:
NSObject *obj = [NSObject alloc];
Class objClass = object_getClass(obj);
Class rootClass = object_getClass(objClass);
Class metaClass = object_getClass(rootClass);
Class metaMetaClass = object_getClass(metaClass);
    
NSLog(@"\n%p instance object \n%p class \n%p metaclass \n%p root metaclass \n%p root metaclass", obj, objClass, rootClass, metaClass, metaMetaClass);

// Print the result
/* 0x100607A20 Instance object 0x7ffF8FAfE118 Class 0x7ffF8FafE0F0 metaclass 0x7ffF8Fafe0F0 Root metaclass 0x7ffF8Fafe0f0 Root metaclass */
Copy the code
  • And then print itWSPersonInformation about the metaclass parent of:
Class pMetaClass = object_getClass(WSPerson.class);
    Class superPMetaClass = class_getSuperclass(pMetaClass);
    NSLog(@"\n%p WSPerson metaclass \nWSPerson metaclass parent: %@ -- %p", pMetaClass, superPMetaClass, superPMetaClass);

// Print the result
/* 0x100008218 WSPerson metaclass Parent of WSPerson metaclass: NSObject -- 0x7ffF8Fafe0f0 */
Copy the code

From here, we can see that the parent of the WSPerson metaclass inherits from the root metaclass of NSObject

Question: Does the metaclass of any class inherit from the root metaclass? Let’s verify again:

WSPerson inherits NSObject
Class tMetaClass = object_getClass(WSTeacher.class);
Class tsuperPMetaClass = class_getSuperclass(tMetaClass);
NSLog(@"\n%p WSTeacher metaclass \nWSTeacher metaclass parent: %@ -- %p", tMetaClass, tsuperPMetaClass, tsuperPMetaClass);

// Print the result
/* 0x100008268 WSTeacher metaclass Parent: WSPerson -- 0x100008218 */
Copy the code

The WSTeacher metaclass descends from the WSPerson metaclass.

New question: 1. What is the parent class of NSObject? 2. What does NSObject’s root metaclass inherit from?

Let’s test it again:

// NSObject
Class nsSuperClass = class_getSuperclass(NSObject.class);
NSLog(@"\nNSObject parent: %@ -- %p", nsSuperClass, nsSuperClass);
    
Class rootMetaSuperClass = class_getSuperclass(rootClass);
NSLog(@"\nNSObject root metaclass: %@ -- %p", rootMetaSuperClass, rootMetaSuperClass);

// Print the result
/* Parent of NSObject: (NULL) -- 0x0 Root metaclass of NSObject: NSObject -- 0x7ffF8FAFe118 */
Copy the code

The original parent class of NSObject is null, and the root metaclass of NSObject still inherits NSObject. To illustrate this graphically:

Merge two graphs

  • Combining the two graphs above gives us a famous flow chart:

Second, the structure of class

  • Last time we looked at the structure of objects, so what is the structure of classes? Let’s go to print and analyze:
(lldb) x/4gx WSPerson.class
0x100008240: 0x0000000100008218 0x00007fff8fafe118
0x100008250: 0x0000000100498290 0x0002801c00000003
Copy the code

Objc4-818.2 objc4-818.2 objc4-818.2 objC4-818.2 objC4-818.2 objC4-818.2 objC4-818.2

  • We know thatClassinheritanceobjc_classAnd then we searched in the source code and found two:
struct objc_class  // OBJC2_UNAVAILABLE

/ / and
struct objc_class : objc_object

Copy the code

Since the first one is OBJC2_UNAVAILABLE, which means objc2 is not available, we chose to analyze the second one, where objc_class inherits objc_Object,

// 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
Copy the code

How can there be an ISA? Let’s go to objc_Object:

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
Copy the code

Isa extends from objc_Object. Objc_class has four member variables. The first is Class ISA, the second is superclass, the third is cache and the fourth is bits.

  • inbits“Is mentioned in the commentsclass_rw_tWhat is this?objc_classThere is an access to it:
class_rw_t *data() const {
    return bits.data();
}
void setData(class_rw_t *newData) {
    bits.setData(newData);
}
Copy the code

It’s retrieved from bits.data(), and if we look at class_rw_t again, we see something familiar:

const method_array_t methods()  // List of methods
const property_array_t properties()  // Attribute list
const protocol_array_t protocols()  // List of agents
Copy the code

This means that the memory data is stored in bits.

So how do we get this data? Let’s keep exploring.

3. Obtain memory data

1. Memory offset

  • Before we dive into getting the memory data from the class, let’s talk about memory offset:
  1. Normal pointer:
int a = Awesome!;
int b = Awesome!;
ysLog(@"\na: %d --- %p", a, &a);
ysLog(@"\nb: %d --- %p", b, &b);

// Print the result
/* a: 666 --- 0x7ffeef2c5c7c b: 666 --- 0x7ffeef2c5c78 */
Copy the code

The pointer to both a and B points to 666, which is called copy, as shown in the figure below:

2. Object pointer

WSPerson *p1 = [WSPerson alloc];
WSPerson *p2 = [WSPerson alloc];
    
ysLog(@"\np1: %@ --- %p", p1, &p1);
ysLog(@"\np2: %@ --- %p", p2, &p2);
// Print the result
/* p1: 
      
        --- 0x7ffeef2c5c70 p2: 
       
         --- 0x7ffeef2c5c68 */
       
      
Copy the code

Here Pointers P1 and p2 point to two objects, as shown in the figure below:

3. array Pointers

int c[4] = {2.4.6.8};
int *d = c;
ysLog(@"\nc: %p -- %p -- %p", &c, &c[0], &c[1]);
ysLog(@"\nd: %p -- %p -- %p", d, d+1, d+2);

// Print the result
/* c: 0x7ffeef2c5c90 -- 0x7ffeef2c5c90 -- 0x7ffeef2c5c94 d: 0x7ffeef2c5c90 -- 0x7ffeef2c5c94 -- 0x7ffeef2c5c98 */
Copy the code

The first address of c is the address of the first element of C, so &c and &c[0] print the same. Here c is assigned to the pointer *d. Why is d+1 the same as &c[1]? What is d+1? Let’s use a diagram to illustrate:



So when I get the memory, it’s the head addressWe'll shift it by oneOne unit here is thetaintType, that is4 bytes. Or you could write it asd+1Here,1It’s one unit. So readC arrayElement can also be written like this:

for (int i = 0; i < 4; i++) {
    int e = *(d + i);
    ysLog(@"%d", e);
}

// Print the result
/* 2, 4, 6, 8 */
Copy the code

From this we can infer that the first address of the class can be shifted by some size to get some content. So let’s verify that.

2. Calculate the translation size

Above we learned about memory pans, next we use memory pans to get bits data. Before bits, there are isa8 bytes, superclass is also 8 bytes (because superclass isa Class type and Class isa pointer type, so it’s 8 bytes), and cache, which is cache_t. Cache_t isa structure that contains many things:

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; / / 8 bytes
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask; / / 4 bytes
#if __LP64__
            uint16_t                   _flags; / / 2 bytes
#endif
            uint16_t                   _occupied; / / 2 bytes
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; // The pointer is 8 bytes}; ...};Copy the code

Most of them are methods, and we know that methods don’t occupy the body of the structure, and some of them are static, and that part is in the global area, and it doesn’t occupy the body of the structure, so just compute the union and _bucketsAndMaybeMask.

  • So let’s calculate it first_bucketsAndMaybeMaskIt is aThe generic:
struct explicit_atomic : public std::atomic<T>

// T is the type to pass in
Copy the code

Its type is determined according to the type passed in, and then we continue to look at the uintptr_t type passed in,

typedef unsigned long           uintptr_t;
Copy the code

Uintptr_t is an unsigned long type, so it accounts for 8 bytes.

  • We looked atunionLet’s look at the structure in_maybeMaskAgain, this depends on the type that’s passed inmask_t, it isuint32_tType, so theta4 bytes
typedef uint32_t mask_t;
Copy the code

Also look at _flags and _occupied, they are both of the uint16_t type, so both are 2 bytes. Based on the nature of the union, we conclude that its size is 8 bytes.

  • Conclusion:cache_tMemory size is16 bytes.So now we have an offset of zero8+8+16 = 32 bytesNext, let’s go get itbitsThe contents of the.

3. TakebitsThe contents of the

  • We get it firstWSPersonAnd then translate it32 bytesIn thehexadecimalIs in the20:
(lldb) x/4gx WSPerson.class
0x100008270: 0x0000000100008248 0x0000000100357140
0x100008280: 0x000000010034f380 0x0000801800000000
(lldb) p/x 0x100008270+0x20
(long) $1 = 0x0000000100008290
Copy the code

Here we get bits, and since bits is class_datA_bits_t, we can print it:

(lldb) p (class_data_bits_t *)0x0000000100008290
(class_data_bits_t *) $2 = 0x0000000100008290
Copy the code

Now that I have the bits, what do I do next? We see an operation in the source code that says,

class_rw_t *data() const {
    return bits.data();
}
Copy the code

Here’s a bits.data() operation, let’s do the same:

(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010107e550
Copy the code

Because $2 is a pointer, the method is called with ->, or if it is a structure.

So we get the class_rw_t pointer, and then we look at the contents of the pointer:

(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000344
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
Copy the code

We have a name property in our WSPerson class, but where is it? Let’s look in class_rw_t. When analyzing the class structure above, we see properties, which is an array type of property_array_t:

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<constclass_ro_t *>(&ro_or_rw_ext)->baseProperties}; }}Copy the code

Properties () from class_rw_t:

(lldb) p $4.properties()
(const property_array_t) $5 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000081c0
      }
      arrayAndFlag = 4295000512}}}Copy the code

So we get the array information. What is property_array_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) { }
};
Copy the code

Property_array_t extends list_array_tt from list_array_tt.

class list_array_tt {
struct array_t {
};

protected:
// Iterator is an iterator method
    class iterator {
        const Ptr<List> *lists;
        const Ptr<List> *listsEnd;
        typename List::iterator m, mEnd;

     public:
        iterator(const Ptr<List> *begin, const Ptr<List> *end)
            : lists(begin), listsEnd(end)
        {
            if(begin ! = end) {// Put both begin and end inside to indicate traversal capabilitym = (*begin)->begin(); mEnd = (*begin)->end(); }}// Fetch the element
        const Element& operator * () const {
            return *m;
        }
        Element& operator * () {
            return*m; }... }... }Copy the code

Let’s continue with the above steps to get the data inside:

(lldb) p $5.list
(const RawPtr<property_list_t>) $6 = {
  ptr = 0x00000001000081c0
}
Copy the code

$6 is an iterator iterated from the above elements. Let’s print its PTR:

(lldb) p $6.ptr
(property_list_t *const) $7 = 0x00000001000081c0
Copy the code

Let’s look at the contents of $7 again:

(lldb) p *$7
(property_list_t) $8 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 1)}Copy the code
  • Here it is concluded that$8Is aProperty_list_t (array)Type, let’s look at its source:
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {};// property_list_t inherits entsize_list_tt
struct entsize_list_tt {
...
    Element& get(uint32_t i) const { 
        ASSERT(i < count);
        returngetOrEnd(i); }... }Copy the code

We can use get to retrieve content, because our class has only one property, so we can use get(0) :

(lldb) p $8.get(0)
(property_t) $9 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
Copy the code

So here we have the name property. The overall process is as follows:

4. Acquisition method

Now that we have the properties of the class, here’s a new guess: Can we get the methods and ivar(instance variables) in a similar way? Let’s try it, first adding some methods, attributes, and instance variables to WSPerson:

@interface WSPerson : NSObject {
    NSString *subject;
}

@property (nonatomic.strong) NSString *name;
@property (nonatomic.strong) NSString *nickName;

- (void)sayNB;
+ (void)sayGood;
@end
Copy the code

Then we get the method:

(lldb) x/4gx WSPerson.class  / / the first step
0x100008338: 0x0000000100008310 0x0000000100357140
0x100008348: 0x000000010034f380 0x0000802800000000
(lldb) p (class_data_bits_t *)0x100008358  // Get the bits address
(class_data_bits_t *) $1 = 0x0000000100008358
(lldb) p $1->data()  // Get the data
(class_rw_t *) $2 = 0x000000010066d780
(lldb) p $2.methods() // Get the array of methods in class_rw_t
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008180
      }
      arrayAndFlag = 4295000448
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->methods()
(lldb) p $3.list // Get an array of Pointers to the list of methods
(const method_list_t_authed_ptr<method_list_t>) $4 = {
  ptr = 0x0000000100008180
}
(lldb) p $4.ptr // Get the PTR
(method_list_t *const) $5 = 0x0000000100008180
(lldb) p *$5  // Step 7 get memory information
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 4294901763.method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 5)
}
(lldb) p $6.get(0)
(method_t) $7 = {}
(lldb) p $6.get(1)
(method_t) $8 = {}
(lldb) p $6.get(2)
(method_t) $9 = {}
(lldb) p $6.get(3)
(method_t) $10 = {}
(lldb) p $6.get(4)
(method_t) $11 = {}
(lldb) 
Copy the code

The previous steps are roughly the same as for properties, but why can’t you get methods at the end? When we look at the print, we see that we have data of type method_t, whereas when we get properties, the properties are of type property_t:

struct property_t {
    const char *name;
    const char *attributes;
};
Copy the code

Property_t has only two members, so it prints the result directly. Let’s look at method_t and find a big structure:

struct big {
    SEL name;
    const char *types;
    MethodListIMP imp;
};
Copy the code

It’s clear that the big structure in method_T is the data that we need.

So we went and tested:

(lldb) p $6.get(0).big
(method_t::big) $12 = {
  name = "sayNB"
  types = 0x0000000100003f81 "v16@0:8"
  imp = 0x0000000100003cd0 (KCObjcBuild`-[WSPerson sayNB])
}
  Fix-it applied, fixed expression was: 
    $6.get(0).big()
(lldb) p $6.get(1).big
(method_t::big) $13 = {
  name = "name"
  types = 0x0000000100003f95 "@ @ 0:8 16"
  imp = 0x0000000100003ce0 (KCObjcBuild`-[WSPerson name])
}
  Fix-it applied, fixed expression was: 
    $6.get(1).big()
(lldb) p $6.get(2).big
(method_t::big) $14 = {
  name = "setName:"
  types = 0x0000000100003f9d "v24@0:8@16"
  imp = 0x0000000100003d00 (KCObjcBuild`-[WSPerson setName:])
}
  Fix-it applied, fixed expression was: 
    $6.get(2).big()
(lldb) p $6.get(3).big
(method_t::big) $15 = {
  name = "setNickName:"
  types = 0x0000000100003f9d "v24@0:8@16"
  imp = 0x0000000100003d50 (KCObjcBuild`-[WSPerson setNickName:])
}
  Fix-it applied, fixed expression was: 
    $6.get(3).big()
(lldb) p $6.get(4).big
(method_t::big) $16 = {
  name = "nickName"
  types = 0x0000000100003f95 "@ @ 0:8 16"
  imp = 0x0000000100003d30 (KCObjcBuild`-[WSPerson nickName])
}
  Fix-it applied, fixed expression was: 
    $6.get(4).big()
Copy the code

We tested the conjecture. But there is a new problem: there are five method displays, and there is only one object method, excluding the setter and getter for the property. Where are the instance object and class methods?

5. Look for instance variables and class methods

The instance variables

Since there are no instance variables in the method, there should be no property list either. Where would that be? Is it another type in class_rw_t? Ro () is a pointer to class_ro_t. Here we see ivar_list_t:

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__ // Indicates that the pointer length is 64 bits, that is, the address length is 64 bits
    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;
    constivar_list_t * ivars; . }Copy the code

I guess the instance variable might be here, so let’s verify:

(lldb) x/4gx WSPerson.class
0x100008338: 0x0000000100008310 0x0000000100357140
0x100008348: 0x000000010034f380 0x0000802800000000
(lldb) p (class_data_bits_t *)0x100008358
(class_data_bits_t *) $1 = 0x0000000100008358
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010195f830
(lldb) p $2.ro()
(const class_ro_t *) $3 = 0x0000000100008138
  Fix-it applied, fixed expression was: 
    $2->ro()
(lldb) p *$3
(const class_ro_t) $4 = {
  flags = 0
  instanceStart = 8
  instanceSize = 32
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char* > ="WSPerson" {
      Value = 0x0000000100003f2e "WSPerson"
    }
  }
  baseMethodList = 0x0000000100008180
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100008200
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008268
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $4.ivars
(const ivar_list_t *const) $5 = 0x0000000100008200
(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 = 0x00000001000082a8
  name = 0x0000000100003f3f "subject"
  type = 0x0000000100003f89 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
Copy the code

Based on the previous experience, we successfully obtained the instance variable subject.

Class method

We used the current class to get the method, but did not find the class method, so where would it be? Class ISA refers to a metaclass. Isa refers to a metaclass. Let’s verify:

(lldb) p/x object_getClass(WSPerson.class) // Get the metaclass
(Class) $0 = 0x0000000100008310 // Bit address of the metaclass
(lldb) p (class_data_bits_t *)0x0000000100008330
(class_data_bits_t *) $1 = 0x0000000100008330
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001022430a0
(lldb) p $2.methods()
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008118
      }
      arrayAndFlag = 4295000344
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->methods()
(lldb) p $3.list
(const method_list_t_authed_ptr<method_list_t>) $4 = {
  ptr = 0x0000000100008118
}
(lldb) p $4.ptr
(method_list_t *const) $5 = 0x0000000100008118
(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $6.get(0).big
(method_t::big) $7 = {
  name = "sayGood"
  types = 0x0000000100003f81 "v16@0:8"
  imp = 0x0000000100003cc0 (KCObjcBuild`+[WSPerson sayGood])
}
  Fix-it applied, fixed expression was: 
    $6.get(0).big()
Copy the code

First we get the metaclass, then we get method based on the previous method, and finally we get the class method sayGood.

conclusion

  1. To obtainattribute: We get the currentclassthebits.dataAnd its type isclass_rw_t“And go looking for itclass_rw_tthepropertiesData, and finally get itproperty_list_tArray, and then according toget()Method to get the property by passing in the location of the property.
  2. To obtainInstance methods: We gotclass_rw_tAfter the data, then go get the insidemethods()“And then work your way in and get itmethod_tAnd then call the data type insidebigWe have the methodInstance methods.
  3. To obtainThe instance variables: We gotclass_rw_tAnd then get the insideroMethod, in getivarsInformation, and then take it step by stepentsize_list_ttArray, and then according toget()Pass in the location and you get itThe instance variables.
  4. To obtainClass method: We got it firstThe metaclassAnd then according toConclusion 2Step, and finally got itClass method.