preface

In the last article, we saw that NSObject is made up of objc_class, so what information does objc_class contain, where do we store all the properties, methods, protocols that we normally use

Objc_class internal structure

Take a look at the source code:

struct objc_class : 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

    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) { bits.setData(newData); }... }Copy the code

Objc_class contains isa, superclass, cache_t, class_data_bits_t, and class_data_bits_t. Class_data_bits_t stores class data and provides a method called data(). So let’s look at the return value class_rw_t of data

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
Copy the code

It contains methods, properties, protocols, and ro attributes. Class_ro_t

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        returnbaseMethodList; }};Copy the code

There’s also some information about the class, so which one is actually storing the information? You can verify it in code

Code validation

You define a class that has variables, attributes, class methods, and instance methods

@interface LGPerson : NSObject{ NSString *hobby; } @property (nonatomic, copy) NSString *nickName; - (void)sayHello; + (void)sayHappy; LGPerson *person = [LGPerson alloc]; Class pClass = object_getClass(person); NSLog(@"% @", pClass);
 }
Copy the code

Get class_rw_t

Open breakpoint debugging, we first output pClass address information

(lldb) x/4gx pClass
0x100001200: 0x001d8001000011d9 0x0000000100afd140
0x100001210: 0x00000001003a0270 0x0000000000000000
Copy the code

To get bits, we need to find the address

Isa // 8 bytes

Class superclass; // 8 bytes

cache_t cache; // 16 bytes

The bits address is offset by 32 bytes from the objc_class address 0x100001200, offset by 32 bytes from the objc_class address 0x100001220

(lldb) p (class_data_bits_t*)0x100001220
(class_data_bits_t *) The $1= 0x0000000100001220 // Get the class_rw_t pointer (LLDB) pThe $1->data()
(class_rw_t *) $2= 0x000000010183e7d0 // Get pointer contents (LLDB) p *$2
(class_rw_t) $3 = {
  flags = 2148139008
  version = 0
  ro = 0x0000000100001178
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x00000001000010c8
        arrayAndFlag = 4294971592
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000100001160
        arrayAndFlag = 4294971744
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSDate
  demangledName = 0x0000000000000000
}
Copy the code

Get the list of methods in class_rw_t

As you can see, methods, properties are all here. Let’s try to look at the list of methods:

lldb) p $3.methods
(method_array_t) $8 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x00000001000010c8
      arrayAndFlag = 4294971592
    }
  }
}
(lldb) p $8.list
(method_list_t *) $9 = 0x00000001000010c8
(lldb) p *$9
(method_list_t) $10 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 3
    first = {
      name = "nickName"
      types = 0x0000000100000f97 "@ @ 0:8 16"
      imp = 0x0000000100000de0 (LGTest`-[LGPerson nickName] at LGPerson.h:16)
    }
  }
}
Copy the code

Get the information from ro

We got the content we wanted, but we wanted to find the internal property of Hobby, and found that it could not be found here. We just looked at the source code and found that ro also has information, so we continue to look

(lldb) p $3.ro
(const class_ro_t *) $4 = 0x0000000100001178
(lldb) p *$4
(const class_ro_t) A $5 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100000f59 "\x02"
  name = 0x0000000100000f50 "LGPerson"
  baseMethodList = 0x00000001000010c8
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100001118
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100001160
}
Copy the code

In addition to baseProperties, baseMethodList, baseProtocols, and Ivars, let’s verify the contents

(lldb) p A $5.ivars
(const ivar_list_t *const) $16 = 0x0000000100001118
(lldb) p *$16
(const ivar_list_t) $17 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0> = {
    entsizeAndFlags = 32
    count = 2
    first = {
      offset = 0x00000001000011c8
      name = 0x0000000100000f7f "hobby"
      type = 0x0000000100000faa "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}
Copy the code

The Hobby property variable is found, and the list of methods is also available here. So, we know that the properties of the class, the instance methods, are stored here.

Class method store location

We find + (void)sayHappy; We haven’t found the storage yet, but we talked about classes that have metaclasses, is that going to be stored in metaclasses, code verification

void testInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}
void testClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy)); // ?
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}
Copy the code

The output is:

0x100002248-0x0-0x0-0x1000021e0

0x0-0x0-0x1000021e0-0x1000021e0

As you can see, class methods exist in the metaclass, we can also verify for ourselves

Current Class Information (LLDB) x/ 4GX pClass 0x1000023B0:0x001D800100002389 0x0000000100AFe140 0x1000023C0: 0x00000001003a1290 0x0000000000000000 // Get metaclass (LLDB) p 0x001D800100002389&0x00007ffffffff8 (long)The $1 = 4294976392
(lldb) po The $1LGPerson // Get the metaclass address (LLDB) x/4gxThe $10x100002388: 0x001d800100afe0f1 0x0000000100afe0f0 0x100002398: 0x000000010192AC00 0x0000000100000003 // Obtain the metaclass class_rw_t (LLDB) p (class_data_bits_t*)0x1000023a8 (class_data_bits_t*)$2 = 0x00000001000023a8
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010192ab60
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2685075456
  version = 7
  ro = 0x00000001000021f8
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x00000001000021d8
        arrayAndFlag = 4294975960
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff95935900
  demangledName = 0x0000000100001f80 "LGPerson"} // get the metaclass ro (LLDB) p$4.ro
(const class_ro_t *) A $5 = 0x00000001000021f8
(lldb) p *S5
error: use of undeclared identifier 'S5'
(lldb) p A $5
(const class_ro_t *) A $5 = 0x00000001000021f8
(lldb) p *A $5
(const class_ro_t) $6 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000000000000
  name = 0x0000000100001f80 "LGPerson"baseMethodList = 0x00000001000021d8 baseProtocols = 0x0000000000000000 ivars = 0x0000000000000000 weakIvarLayout = 0x0000000000000000 baseProperties = 0x0000000000000000} // Get the list of metaclass methods (LLDB) p$6.baseMethodList
(method_list_t *const) $7 = 0x00000001000021d8
(lldb) p *$7
(method_list_t) $8 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayHappy"
      types = 0x0000000100001f8b "v16@0:8"
      imp = 0x0000000100001bc0 (LGTest`+[LGPerson sayHappy] at LGPerson.m:17)
    }
  }
}
Copy the code

As you can see from the information, class methods do exist in metaclasses.

To summarize, the internal structure of a class