Summary of ios low-level articles

1. Objects, ISA, classes and metaclasses

Concept of 1.1.

  • eachObjects have an ISAWhen the object allocates memory, the memory of0 to 7 bytesWhat’s stored isIsa information
  • Objects are contained in the ISA of objectsBelongs to the classThe information of
  • There is also an ISA for each class, and there is also a class memory0 to 7 bytesBecause theA class is also an object.Class is an object of a metaclassMetaclasses are rightClass object description, just as a class is a description of a common instance object,Classes belong to metaclasses
  • metaclassDefinition and CreationBy compilingTranslation is done, metaclasses are associated with classes, names andThe name of the class of the same name.
  • The root metaclass of NSObjectPoint to theNSObject metaclass.The ISA of the root metaclass points to itself

The address of the root metaclass is 0x00000001003340F0 and the isa & Mask of the root metaclass = ISA (0x00000001003340F0&0x00007ffffffffff8ull = 0x00000001003340F0), verifies that the root metaclass isa points to itself

1.2 Inheritance relationship

  • In the OC,NSObject's parent class is nilIf theClass has inheritanceDepartment,Class metaclasses also have inheritance relationships

Such as:

When you have a Student class that inherits from Person, and a Person class that inherits from NSObject, the Student metaclass inherits from the Person metaclass, and the Person metaclass inherits from NSObject, and the metaclass of NSObject is NSObject itself

  • Inheritance relationships between classes:

    • Class (ttf_subclass)Inherited fromsuperClass
    • superClassInherited fromRoot Class, where the root class refers toNSObject
    • The root classInherited fromnil, soThe root classnamelyNSObjectCan be understood asOrigin of all things, that is, out of thin air
  • Metaclasses also have inheritance. The inheritance relationship between metaclasses is as follows:

    • SubClass metaclass (Metal SubClass)Inherited fromSuperClass (metal SuperClass)
    • SuperClass (metal SuperClass)Inherited fromRoot metal Class
    • Root Metal ClassInheritance inRoot class, where the root class refers toNSObject
  • Object, class, metaclassInheritance relationships: The inheritance relationship isAs opposed to classes and metaclasses.There is no inheritance relationship between objects

Photo from LG-Cooci teacher

Therefore,**The placement of ISA** The figure is as follows:

  • Instance of SubclasstheisaPoint to theClass (class)
  • Class object (class) isaPoint to theMeta Classes
  • Meta ClassestheisaPoint to theRoot Metal class
  • Root Metal class 的isaPointing to itoneselfIn itself, formThe closed loopHere,A metaclassisNSObject

Example analysis: Person inherits from NSObject, Student inherits from Person

  • Isa routing chain (two)

    • Student’s ISA chain:Student --> student --> student --> NSObject --> NSObject --> NSObject --> NSObject
    • Isa bitmap for Person:Person --> person --> Person --> NSObject --> NSObject --> person --> person --> Person --> NSObject
  • Superclass Chain (two)

    • Class inheritance chain:Student --> Person --> NSObject --> nil
    • Metaclass inheritance chain:Student --> Person --> NSObject --> NSObject --> nil

Only one copy of the 1.3 class exists in memory after it is compiled

//MARK: - Analyze the number of classes in memory
void lgTestClassNum(){
    Class class1 = [HTPerson class];
    Class class2 = [HTPerson alloc].class;
    Class class3 = object_getClass([HTPerson alloc]);
    Class class4 = [HTPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
    /* The result is 0x100008100-0x100008100-0x100008100-0x100008100 */

}
Copy the code

In the code above, we create three LGPerson class objects in three different ways, but the address of the three class objects is exactly the same, indicating that the class has only one memory.

Note: Special NSObjec class

    // NSObject instance object
       NSObject *object1 = [NSObject alloc];
        / / NSObject class
        Class class = object_getClass(object1);
        / / NSObject metaclass
        Class metaClass = object_getClass(class);
        // NSObject root metaclass
        Class rootMetaClass = object_getClass(metaClass);
        // NSObject Root metaclass
        Class rootRootMetaClass = object_getClass(rootMetaClass);
        NSLog(@"\n%p instance object \n%p class \n%p metaclass \n%p root metaclass \n%p root metaclass",object1,class,metaClass,rootMetaClass,rootRootMetaClass);

    /* Output: 0x10070D950 Instance object 0x100357140 Class 0x1003570F0 metaclass 0x1003570F0 Root metaclass 0x1003570F0 Root metaclass */
Copy the code

There’s only one NSObject class in memory, and the metaclass of NSObject has the same name as the class, and there’s one, and the metaclass of NSObject has the same metaclass, the root metaclass, the root metaclass, and the ISA of the metaclass of NSObject points to itself

2 objc_object and objc_class

  • All classes are objects of objC_class, which inherits from the structure objc_Object

// Note: the source code is from objC4-781 objc_runtime_new.h
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;            
    class_data_bits_t bits;    

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

Note: source code from objC4-781 objC_runtime_new.h

What is the relationship between objc_class and objc_Object?

  • The structure type objc_class inherits from objc_Object, which is also a structure and has an ISA property, so objc_class also has an ISA property

  • In mian. CPP, the ISA in NSObject is defined at the bottom by Class, where the underlying encoding of Class comes from the objc_class type, so NSObject also has isa attributes

  • NSObject isa class that initializes an instance object, objc, which satisfies the properties of objc_object (that is, it has the isa property), mainly because isa is inherited from objc_class by NSObject, Objc_class inherits from Objc_Object, which has an ISA property. So objects have an ISA, and ISA stands for pointing, from the current objc_Object

  • Objc_object is the current root object, and all objects have the property objc_Object, which has the ISA property

  • Objc_object isThe root object, is a structure, objc_ObjectContains an ISASoMetaclasses, classes, and objects all have ISA
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
    
Copy the code

Objc_object relation to objects:

  • All objects are instantiated from the objc_Object template; The root class of the OC-side class is NSObject, and the underlying template for NSObject is objC_Object

  • OC code is compiled into C++ code, and the underlying template for Class(the type of the isa pointer) in OC is objc_class:

typedef struct objc_class *Class;

Three types of structural analysis

3.1 Memory Offset

Contiguous data addresses exist in stack memory:

  • ordinaryA variable of the basic typeAddress offset of
 int a = 10;
 int b = 10;
 printf("%d--%p\n",a,&a);
 printf("%d--%p\n",b,&b);
  /* Prints the result: 10-- 0x7ffeefBff4f0 10-- 0x7FFeefBff4F4 */
Copy the code

Int b = 10; int a = 10; int b = 10; int b = 10;

A and B are stored on the stack, and their addresses are contiguous. The addresses correspond to the memory space required by the type of A. In this example, A is an int, and all the addresses of A and B differ by 4 bytes

  • Class object variableAddress offset
 LGPerson *p1 = [LGPerson alloc]; // p1 is the pointer
 LGPerson *p2 = [LGPerson alloc];
 NSLog(@"%@ -- %p\n", p1, &p1);
 NSLog(@"%@ -- %p\n", p2, &p2);
/* Print result: 
      
        -- 0x7ffeefbff4e0 
       
         -- 0x7ffeefbff4e8 */
       
      

Copy the code

[LGPerson alloc] [LGPerson alloc] [LGPerson alloc] [LGPerson alloc] [LGPerson alloc] [LGPerson alloc

  • Pointer to an arrayAddress offset
 int c[4] = {1.2.3.4};
 int *d = c;
 NSLog(@"%p -- %p - %p",&c[0], &c[1],&c[2]);
 NSLog(@"%p -- %p - %p", d, d+1, d+2);
 /* 0x7ffeefbff4e0 -- 0x7ffeefbff4e4 - 0x7ffeefbff4e8 0x7ffeefbff4e0 -- 0x7ffeefbff4e4 - 0x7ffeefbff4e8 */
Copy the code

The addresses of array elements are contiguous, and the difference between &c[0] and &c[1] is 4 bytes, and the difference between &c[1] and &c[2] is 4 bytes, depending on the data type stored

Other elements in the array can be retrieved by using the first address + the offset, where the offset is the subscript of the array. The actual address is the first address + the offset * number of bytes of the data type

3.2 Objc_class Class structure

Objc4-781 Project Headers/objc_runtime-new.h Define the objc_class structure as follows:

  • Isa attribute: Isa of Class type, 8 bytes

  • Superclass property: Class type, Class is an objc_object pointer, 8 bytes

  • Cache property: Cache_t Object of the structure type, occupying 16 bytes

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t* > _buckets; // is a structure pointer type of 8 bytes
    explicit_atomic<mask_t> _mask; // mask_t is of type mask_t, which is the 4-byte alias of an unsigned int
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets; // Is a pointer, accounting for 8 bytes
    mask_t _mask_unused; // is of type mask_t, which is an alias of the uint32_t type definition and takes up 4 bytes
    
#if __LP64__
    uint16_t _flags;  // is of the uint16_t type, uint16_t is an alias for unsigned short and takes up 2 bytes
#endif
    uint16_t _occupied; // is of the uint16_t type, uint16_t is an alias for unsigned short and takes up 2 bytes
Copy the code

#if branches compute memory

Buckets is struct bucket_t *, which is an 8-byte structure pointer

Mask is of type mask_t, and mask_t, the alias of an unsigned int, is 4 bytes

_flags is of type uint16_t and takes up 2 bytes

_occupied is of the uint16_t type, comprising 2 bytes

Elseif branches compute memory

_maskAndBuckets is a uintptr_t type and is a pointer with 8 bytes

_mask_unused is of the mask_t type, which is an alias to the uint32_t type definition and takes up four bytes

_flags is of type uint16_t and takes up 2 bytes

_occupied is of the uint16_t type, comprising 2 bytes

  • bitsAttribute: class_datA_bits_t. It is a structure. The size of the structure’s memory needs to be determined according to its internal attributesBits first address

3.3 Obtaining the Bits Address

3.3.1 throughThe first address of the class is shifted by 32 bytesGet class_data_bits_t * :

(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) x/4gx 0x0000000100002240
0x100002240: 0x0000000100002218 0x0000000100334140
0x100002250: 0x00000001006847d0 0x000580240000000f
(lldb) p 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) The $1 = 4294976024
(lldb) p/x 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100002218
(lldb) p (class_data_bits_t*)0x0000000100002238
(class_data_bits_t *) $3 = 0x0000000100002238

Copy the code

3.3.2 class_datA_bits_t Gets the pointer to the class_rw_T structure by using the data() method

  • Class_data_bits_t structure

  • Command to obtain:
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) x/4gx 0x0000000100002240
0x100002240: 0x0000000100002218 0x0000000100334140
0x100002250: 0x00000001006847d0 0x000580240000000f
(lldb) p 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) The $1 = 4294976024
(lldb) p/x 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100002218
(lldb) p (class_data_bits_t*)0x0000000100002238
(class_data_bits_t *) $3 = 0x0000000100002238
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100683dc0
(lldb) p $4->methods()
Copy the code

3.3.4 Class_rw_t Obtains the method list, attribute list, and protocol list from methods(),properties(), and protocols()

  • Class_rw_t structure definition

  • Command to get a list of class methods (class methods are stored in metaclass, retrieved from metaclass)
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) x/4gx 0x0000000100002240
0x100002240: 0x0000000100002218 0x0000000100334140
0x100002250: 0x00000001006847d0 0x000580240000000f
(lldb) p 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) The $1 = 4294976024
(lldb) p/x 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100002218
(lldb) p (class_data_bits_t*)0x0000000100002238
(class_data_bits_t *) $3 = 0x0000000100002238
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100683dc0
(lldb) p $4->methods()
(const method_array_t) A $5 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002090
      arrayAndFlag = 4294975632
    }
  }
}
(lldb) p A $5.list
(method_list_t *const) $6 = 0x0000000100002090
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayBye"
      types = 0x0000000100000f6e "v16@0:8"
      imp = 0x0000000100000d20 (KCObjc`+[LGPerson sayBye])
    }
  }
}
(lldb) p $7.get(0)
(method_t) $8 = {
  name = "sayBye"
  types = 0x0000000100000f6e "v16@0:8"
  imp =
Copy the code
  • Command to get object methods (from the class structure)
lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) p/x (class_data_bits_t *)(0x0000000100002260)
(class_data_bits_t *) The $1 = 0x0000000100002260
(lldb) p/x The $1->data()
(class_rw_t *) $2 = 0x0000000100656070
(lldb) p/x $2->methods()
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x00000001000020f8
      arrayAndFlag = 0x00000001000020f8
    }
  }
}
(lldb) p $3.list
(method_list_t *const) $4 = 0x00000001000020f8
(lldb)  p *$4
(method_list_t) $8 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 16
    count = 2
    first = {
      name = "name"
      types = 0x0000000100000ee1 "T@\"NSString\",C,N,V_name"
      imp = 0x0000000100000ef9 ("age")
    }
  }
}
(lldb)p $8.get(0)
(method_t) $9 = {
  name = "name"
  types = 0x0000000100000ee1 "T@\"NSString\",C,N,V_name"
  imp = 0x0000000100000ef9 ("age")
}
(lldb) p $8.get(1)
(method_t) $10 = {
  name = "age"
  types = 0x0000000100000efd "Tq,N,V_age"
  imp = 0x0000000100002240 ((void *)0x0000000100002218: LGPerson)
}
(lldb) p $8.get(2)
Assertion failed: (i < count), functionGet the file/Users/wangqiao/Desktop/iOSV9.0 / can compile objc source/runtime/objc runtime - new. H, line 434. Error: Execution was interrupted, reason: signal SIGABRT. The process has been returned to the state before expression evaluation.Copy the code
  • Command to get object properties (from the class structure)
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) p/x (class_data_bits_t *)(0x0000000100002260)
(class_data_bits_t *) The $1 = 0x0000000100002260
(lldb) p/x The $1->data()
(class_rw_t *) $2 = 0x0000000100656070
(lldb) p/x $2->properties()
(const property_array_t) A $5 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x00000001000021d8
      arrayAndFlag = 0x00000001000021d8
    }
  }
}
(lldb) p/x A $5.list
(property_list_t *const) $6 = 0x00000001000021d8
(lldb) p *$6
(property_list_t) $7 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 2
    first = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
  }
}
(lldb) p $7.get(0)
(property_t) $11 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $12 = (name = "age", attributes = "Tq,N,V_age")
(lldb) p $7.get(2)
Assertion failed: (i < count), functionGet the file/Users/wangqiao/Desktop/iOSV9.0 / can compile objc source/runtime/objc runtime - new. H, line 434. Error: Execution was interrupted, reason: signal SIGABRT. The process has been returned to the state before expression evaluation. (lldb)Copy the code
  • Command to get a list of properties (without member variables)
    • The address of bits is 32 bytes cheaper than the address of the objc_class classclass_data_bits_tThe address of theclass_data_bits_t*
    • inclass_data_bits_tIn the calldata()To obtain theclass_rw_tThe address of theclass_rw_t*
    • class_rw_tStruct callro()To obtain theclass_ro_tThe address of the ` class_ro_t *)
    • The baseProperties member variable in the class_ro_t structure is the address of the property list

  • Obtain the value from the LLVM commandProperty listFor the same reasonGets a list of member variablesandMethods list)

  • Get ivars with bits->data->ro (containing attributes and member variables)
(lldb) x/4gx person
0x100647220: 0x011d8001000083dd 0x0000000000000000
0x100647230: 0x0000000000000000 0x0000000000000000
(lldb) p 0x011d8001000083dd & 0x00007ffffffffff8ULL
(unsigned long long) The $1 = 4295001048
(lldb) p/x  0x011d8001000083dd & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000083d8
(lldb) p (class_data_bits_t *)0x00000001000083f8
(class_data_bits_t *) $3 = 0x00000001000083f8
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100646670
(lldb) p $4->ro()
(const class_ro_t *) A $5 = 0x0000000100008098
(lldb) p *A $5
(const class_ro_t) $6 = {
  flags = 388
  instanceStart = 8
  instanceSize = 72
  reserved = 0
   = {
    ivarLayout = 0x0000000100003ede "\x01\x11\x13"
    nonMetaclass = 0x0000000100003ede
  }
  name = {
    std::__1::atomic<const char *> = "HTPerson" {
      Value = 0x0000000100003ed5 "HTPerson"
    }
  }
  baseMethodList = 0x00000001000080e0
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000100008190
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100008298
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $6.ivars
(const ivar_list_t *const) $7 = 0x0000000100008190
(lldb) p $7->get(0)
(ivar_t) $8 = {
  offset = 0x0000000100008370
  name = 0x0000000100003eec "hobby"
  type = 0x0000000100003f60 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
 
(lldb) p $7->get(1)
(ivar_t) $9 = {
  offset = 0x0000000100008378
  name = 0x0000000100003ef2 "weight"
  type = 0x0000000100003f6c "i"
  alignment_raw = 2
  size = 4
}
 
(lldb) p $7->get(2)
(ivar_t) $10 = {
  offset = 0x0000000100008380
  name = 0x0000000100003ef9 "objc"
  type = 0x0000000100003f6e "@\"NSObject\""
  alignment_raw = 3
  size = 8
}
 
(lldb) p $7->get(3)
(ivar_t) $11 = {
  offset = 0x0000000100008388
  name = 0x0000000100003efe "sex"
  type = 0x0000000100003f7a "c"
  alignment_raw = 0
  size = 1
}
 
(lldb) p $7->get(4)
(ivar_t) $12 = {
  offset = 0x0000000100008390
  name = 0x0000000100003f02 "intersting"
  type = 0x0000000100003f60 "@\"NSString\""
  alignment_raw = 3
  size = 8
}

(lldb) p $7->get(5)
(ivar_t) $13 = {
  offset = 0x0000000100008398
  name = 0x0000000100003f0d "_name"
  type = 0x0000000100003f60 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7->get(6)
(ivar_t) $14 = {
  offset = 0x00000001000083a0
  name = 0x0000000100003f13 "_nickname"
  type = 0x0000000100003f60 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7->get(7)
(ivar_t) $15 = {
  offset = 0x00000001000083a8
  name = 0x0000000100003f1d "_age"
  type = 0x0000000100003f7c "q"
  alignment_raw = 3
  size = 8
}
Copy the code

3.4 OC layer obtains instance variable names and attribute names through Runtime

void lgObjc_copyIvar_copyProperies(Class pClass){
   
   unsigned int count = 0;
   Ivar *ivars = class_copyIvarList(pClass, &count);
   for (unsigned int i=0; i < count; i++) {
       Ivar const ivar = ivars[i];
       // Get the instance variable name
       const char*cName = ivar_getName(ivar);
       NSString *ivarName = [NSString stringWithUTF8String:cName];
       LGLog(@"class_copyIvarList:%@",ivarName);
   }
   free(ivars);

   unsigned int pCount = 0;
   objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
   for (unsigned int i=0; i < pCount; i++) {
       objc_property_t const property = properties[i];
       // Get the attribute name
       NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
       
       LGLog(@"class_copyProperiesList:%@",propertyName);
   }
   free(properties);
}
Copy the code