A, class memory RO data

In the previous article, you could see attributes and instance methods in a class structure through LLDB, but not instance variables and class methods

@interface ApplePerson : NSObject{
    NSString *salary;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *workNumber;

- (void)sayHenXi;
+ (void)sayNiuBi;
@end
Copy the code

We look through the source code to find the instance variable ivars(Instance Variables) in the structure class_ro_t

Struct class_ro_t {...void *baseMethodList;
    protocol_list_t * baseProtocols;
    constivar_list_t * ivars; ... }Copy the code

We looked through apple developer videos to prove this idea

Looking at the source again, I find a method to return the class_ro_t structure

const class_ro_t *ro() const{... }Copy the code

Instance variables and attributes and encodings

Next test, print memory

(lldb) x/6gx ApplePerson.class
0x100008498: 0x0000000100008470 0x0000000100379140
0x1000084a8: 0x000000010036d0e0 0x0000803000000000
0x1000084b8: 0x00000001007a3024 0x00000001000084e8
Copy the code

Shift 32 bytes

(lldb) p/x 0x100008498 + 0x20
(long) $1 = 0x00000001000084b8
Copy the code

Strong transfer printing

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

To get the data

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

Get ro

(lldb) p *$3.ro()
(const class_ro_t) $5= {... ivars =0x00000001000080d0. }Copy the code

You can see ivars. Go ahead

(lldb) p $5.ivars
(const ivar_list_t *const) $6 = 0x00000001000080d0
Copy the code

Print $6

(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

You can see that $7 is a list, so get is used to get the data

(lldb) p $7.get(0)
(ivar_t) $8 = {
  offset = 0x0000000100008450
  name = 0x0000000100003e90 "salary"
  type = 0x0000000100003f4c "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
  offset = 0x0000000100008454
  name = 0x0000000100003e97 "age"
  type = 0x0000000100003f58 "q"
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
  offset = 0x0000000100008458
  name = 0x0000000100003e9b "_name"
  type = 0x0000000100003f4c "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(3)
(ivar_t) $11 = {
  offset = 0x000000010000845c
  name = 0x0000000100003ea1 "_workNumber"
  type = 0x0000000100003f4c "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(4)
Assertion failed: (i < count), function get.file /Users/... /runtime/objc-runtime-new.h.line 624.
error: Execution was interrupted.reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
Copy the code

Salary, age, _name, _workNumber ivars can be obtained to compare the class structure

@interface ApplePerson : NSObject{
    NSString *salary;
    NSInteger age;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *workNumber;
Copy the code

You can see that instance variables exist intact in ivars, and attributes also exist in ivars, but underlined _. NSString is @\”NSString\” size = 8, which is 8 bytes, while NSInteger is “q”, size = 8, which indicates that NSInteger is also 8 bytes in 64-bit systems

Third, check the implementation of C++ code

Because the source code too much, view inconvenient, create an empty command line project

#import <Foundation/Foundation.h>

@interface ApplePerson : NSObject{
    NSString *salary;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *workNumber;

- (void)sayHenXi;
+ (void)sayNiuBi;
@end

@implementation ApplePerson

- (void)sayHenXi{
}
+ (void)sayNiuBi{
    
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
    }
    return 0;
}
Copy the code

Clang clang-rewrite-objc main.m -o niubi. CPP, to get the CPP file, search ApplePerson

Finding that the attributes and methods you define are underlying and masked, the attributes name and workNumber generate instance variables with underscores in the ApplePerson_IMPL structure, with set/get methods

You can also see the list of methods

There are v16@0:8 and v24@0:8@16 which belong to OC type codes

Objective-C Runtime Programming Guide

Code Meaning
c char
i An int
s short
l long``l is treated as a 32-bit quantity on 64-bit programs.
q long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f float
d double
B A C++ bool or a C99 _Bool
v void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[array type] An array
{name=type… } A structure
(name=type… ) A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

v16@0: the v of 8 stands for void; 16 indicates the memory size occupied by a parameter. @ represents an object (either statically typed ID or typed ID) which is really the id self; 0 means to start at position 0. : Method selector (SEL); 8 means starting at position 8, id self is structure pointer type 8 bytes, SEL is 8 bytes, exactly 16 bytes

4. Low-level implementation of setter methods

Next, let’s examine the impact of attributes on the underlying code

@interface ApplePerson : NSObject{
    NSString *salary;
}
//@property (nonatomic, copy) NSString *name;
//@property (nonatomic, copy) NSString *workNumber;

@property (nonatomic, copy) NSString *nc_job_title;
@property (atomic, copy)    NSString *ac_job_title;
@property (nonatomic)       NSString *n_job_title;
@property (atomic)          NSString *a_job_title;
Copy the code

A clang


static void _I_ApplePerson_setNc_job_title_(ApplePerson * self, SEL _cmd, NSString *nc_job_title) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ApplePerson, _nc_job_title), (id)nc_job_title, 0.1); }

static void _I_ApplePerson_setAc_job_title_(ApplePerson * self, SEL _cmd, NSString *ac_job_title) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ApplePerson, _ac_job_title), (id)ac_job_title, 1.1); }

static void _I_ApplePerson_setN_job_title_(ApplePerson * self, SEL _cmd, NSString *n_job_title) { (*(NSString **)((char *)self + OBJC_IVAR_$_ApplePerson$_n_job_title)) = n_job_title; }

static void _I_ApplePerson_setA_job_title_(ApplePerson * self, SEL _cmd, NSString *a_job_title) { (*(NSString **)((char *)self + OBJC_IVAR_$_ApplePerson$_a_job_title)) = a_job_title; }
Copy the code

As you can see, properties with copy in the underlying objc_setProperty method, without copy, there is no source code to read objc_setProperty

In either case, reallySetProperty(self, _cmd, newValue, offset, true, false, false) is called;

Check the reallySetProperty

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy){...if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return; newValue = objc_retain(newValue); }... }Copy the code

Copy, copy, copy, copy, copy, copy, copy, copy

@interface ApplePerson : NSObject{
    NSString *salary;
}
@property (nonatomic, copy) NSString *nc_job_title;
@property (nonatomic, strong) NSString *ns_job_title;
Copy the code

A clang

static void _I_ApplePerson_setNc_job_title_(.) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ApplePerson, _nc_job_title), (id)nc_job_title, 0.1); }

static void _I_ApplePerson_setNs_job_title_(.) { (*(NSString **)((char *)self + OBJC_IVAR_$_ApplePerson$_ns_job_title)) = ns_job_title; }

Copy the code

As you can see, copy uses objc_setProperty, while strong uses memory translation, and you can see the difference here

Storage of class methods

In the last article, we only saw the instance method at the bottom, not the class method. If we pull the execution file into the rotten apple, we can see the + sayNiuBi method

Look at the code behind Clang

static void _I_ApplePerson_sayHenXi(ApplePerson * self, SEL _cmd){}static void _C_ApplePerson_sayNiuBi(Class self, SEL _cmd){}Copy the code

I should be Instance method, C should be Class methods, and you can see that it’s passed in as an argument, one is ApplePerson *, and the other is Class, which is the next level up from ApplePerson, which is the metaclass of ApplePerson, Next, verify the source code

// Prints the memory address
(lldb) x/4gx ApplePerson.class
0x100008470: 0x0000000100008448 0x0000000100379140
0x100008480: 0x000000010036d0e0 0x0000802800000000
/ / get the isa
(lldb) p/x 0x0000000100008448 & 0x007ffffffffffff8
(long) $1 = 0x0000000100008448
// Get the metaclass address
(lldb) po 0x0000000100008448
ApplePerson
Copy the code

Translation of 32

(lldb) p/x 0x0000000100008448 + 0x20
(long) $3 = 0x0000000100008468
Copy the code

reduction

(lldb) p/x (class_data_bits_t *)$3
(class_data_bits_t *) $4 = 0x0000000100008468
Copy the code

To get the data

(lldb) p $4->data()
(class_rw_t *) $5 = 0x00000001011a5600
Copy the code

To view

(lldb) p *$5
(class_rw_t) $6 = {
  flags = 2684878849
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4302404417
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00000001fe029dc0
}
Copy the code

Access method

(lldb) p $6.methods()
(const method_array_t) $7 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003d20
      }
      arrayAndFlag = 4294982944}}}Copy the code

Get method list

(lldb) p $7.list
(const method_list_t_authed_ptr<method_list_t>) $8 = {
  ptr = 0x0000000100003d20
}
Copy the code

Get PTR

(lldb) p $8.ptr
(method_list_t *const) $9 = 0x0000000100003d20
Copy the code

Check the $9

(lldb) p *$9
(method_list_t) $10 = {
  entsize_list_tt<method_t, method_list_t, 4294901763.method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 1)}Copy the code

Get method name

(lldb) p $10.get(0).name
(SEL) $11 = "sayNiuBi"
  Fix-it applied, fixed expression was: 
    $10.get(0).name()
Copy the code

Finally, the class method sayNiuBi is found in the metaclass

Class method storage API way parsing

The new project starts by defining a class through API validation of classes and metaclass methods

@interface ApplePerson : NSObject{
    NSString *salary;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *workNumber;

- (void)sayHenXi;
+ (void)sayNiuBi;
Copy the code

Defines a method to get a method name

void AppleObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        // Get the method name
        NSString *key = NSStringFromSelector(method_getName(method));
        NSLog(@"Method, name: %@", key);
    }
    free(methods);
}
Copy the code

Methods are called separately by class and metaclass

ApplePerson *person = [ApplePerson alloc];
Class pClass     = object_getClass(person);
AppleObjc_copyMethodList(pClass);
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
AppleObjc_copyMethodList(metaClass);
Copy the code

A printout

Method, name: sayHenXi
Method, name: name
Method, name: setName:
Method, name: workNumber
Method, name: setWorkNumber:
Method, name: .cxx_destruct
------------------------------
Method, name: sayNiuBi
Copy the code

The set/get methods, c++ destructor methods and instance methods of the above class print attributes; The metaclass prints the class methods, which are then verified by instance methods

void AppleInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method instance_Method1 = class_getInstanceMethod(pClass, @selector(sayHenXi));
    Method instance_Method2 = class_getInstanceMethod(metaClass, @selector(sayHenXi));

    Method instance_Method3 = class_getInstanceMethod(pClass, @selector(sayNiuBi));
    Method instance_Method4 = class_getInstanceMethod(metaClass, @selector(sayNiuBi));
    
    NSLog(@"\nfun : %s \ninstance_Method1 : %p \ninstance_Method1 : %p \ninstance_Method1 : %p\ninstance_Method1 : %p",__func__,instance_Method1,instance_Method2,instance_Method3,instance_Method4);
}
Copy the code

call

ApplePerson *person = [ApplePerson alloc];
Class pClass     = object_getClass(person);
AppleInstanceMethod_classToMetaclass(pClass);
Copy the code

A printout

fun : AppleInstanceMethod_classToMetaclass 
instance_Method1 : 0x100003df1 
instance_Method1 : 0x0 
instance_Method1 : 0x0
instance_Method1 : 0x100003dd9
Copy the code

Class instance method output address 0x100003DF1, metaclass instance method output 0x100003DD9, verified by class method

void AppleClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method class_method1 = class_getClassMethod(pClass, @selector(sayHenXi));
    Method class_method2 = class_getClassMethod(metaClass, @selector(sayHenXi));

    Method class_method3 = class_getClassMethod(pClass, @selector(sayNiuBi));
    Method class_method4 = class_getClassMethod(metaClass, @selector(sayNiuBi));
    
    NSLog(@"\nfun : %s \nclass_method1 : %p \nclass_method2 : %p \nclass_method3 : %p\nclass_method4 : %p",__func__,class_method1,class_method2,class_method3,class_method4);
}
Copy the code

call

ApplePerson *person = [ApplePerson alloc];
Class pClass     = object_getClass(person);
AppleClassMethod_classToMetaclass(pClass);
Copy the code

A printout

fun : AppleClassMethod_classToMetaclass 
class_method1 : 0x0 
class_method2 : 0x0 
class_method3 : 0x100003d61
class_method4 : 0x100003d61
Copy the code

Class_method4 is a metaclass method, class_method4 is an object method of a metaclass, how can I get a method of a metaclass, and I can see that the class method is 0x100003D61, and the metaclass class method is 0x100003D61

Method class_getClassMethod(Class cls, SEL sel)
{
    if(! cls || ! sel)return nil;
    return class_getInstanceMethod(cls->getMeta(), sel);
}
Copy the code

When passed in a metaclass, the instance method of the metaclass is returned, so it is consistent to find the implementation of the method by getting IMP

void AppleIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHenXi));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHenXi));
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayNiuBi));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayNiuBi));

    NSLog(@"\n %s \n imp1:%p\n imp2:%p\n imp3:%p\n imp4:%p",__func__,imp1,imp2,imp3,imp4);
}
Copy the code

call

ApplePerson *person = [ApplePerson alloc];
Class pClass     = object_getClass(person);
AppleIMP_classToMetaclass(pClass);
Copy the code

A printout

 AppleIMP_classToMetaclass 
 imp1:0x100003a00
 imp2:0x19305f6c0
 imp3:0x19305f6c0
 imp4:0x100003a14
Copy the code

Imp1 and imp4 are sometimes correct. Imp2 and imp3 should theoretically be nil

IMP class_getMethodImplementation(Class cls, SEL sel){ IMP imp; ...if(! imp) {return _objc_msgForward;
    }
    return imp;
}
Copy the code

So OC actually has no so-called class methods, in fact are instance methods