Welcome to the iOS Basics series (suggested in order)



IOS low-level – Alloc and init explore


IOS Bottom – Isa for everything


IOS low-level – Analysis of the nature of classes


IOS Underlying – cache_t Process analysis


IOS basics – Analysis of the nature of methods and search processes


1. Overview of this paper

  • This article mainly analyzes from the bottom layerisaThe role of,isaThe data structure of,isaWhat is actually stored in each location within, andisaWhether innonpointerThe difference between the
  • isaBitmap of,SuperClassThe point to


Note: ISA is an important clue connecting objects and classes. If you understand ISA, you can have a deeper understanding of the nature of objects and the trend of class methodsCopy the code


2. The role of the isa

As mentioned in the previous article, alloc also initializes isa after opening up space to associate objects with classes. Therefore, for objects, the basic function of ISA is to bind with the class and tell the system where the object belongs. But most Of Nonpointer’s ISAs do more than just point; they store a lot of information inside.

Here we introduce the concept of nonpointer.

The early call
isaYou can just go back to the class, and then Apple, in order to optimize memory, added an extremely rich information inside it, and added
isa_maskTo not get the class directly. The ones that are optimized are the ones mentioned
nonpointer, is also the focus of this paper.

Verify this statement by initializing the isa source code

objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { assert(! isTaggedPointer());if(! nonpointer) { isa.cls = cls; }else{ assert(! DisableNonpointerIsa); assert(! cls->instancesRequireRawIsa()); isa_t newisa(0);#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endifisa = newisa; }}Copy the code

First predicate judgment

assert(! isTaggedPointer());Copy the code

If it is TaggedPointer, it is not executed and isa is no longer a concept.

Here we introduce the concept of TaggedPointer:

In the early 64-bit systems, when we stored the underlying data types, the underlying data was encapsulated into
NSNumber
Object, which also takes 8 bytes
Memory, 4 bytes for 32-bit machines.
To store and access one
NSNumber
Object that needs to allocate memory on the heap, maintain its reference count, and manage its lifetime. All this adds extra logic to the program, resulting in a loss of efficiency.
So if there is no extra processing, there is a lot of wasted space.

Therefore, Apple introduced TaggedPointer. When the object pointer is of type TaggedPointer, the pointer value is not the address but the real value, which directly optimized the storage and improved the acquisition speed.

The characteristics of TaggedPointer

  • Designed to store small objects, such asNSNumberAnd part of theNSString
  • Instead of storing addresses, Pointers store the value of objects directly. Therefore, it is not an object, but a normal variable masquerading as an object. Memory is also not in the heap, but in the stack, managed by the system, and not neededmallocandfree
  • Three times more efficient at memory reads and 106 times faster at creation. (lessmallocProcess, fetch directly from the address)

Going back to the source code,

if (! nonpointer) {isa.cls = cls; }

This verifies that when ISA pointer optimization is disabled, ISA is directly associated with the class without subsequent operations. When optimization is turned on, isa_t is initialized

isa_t newisa(0);Copy the code

, then assign values to the inner attributes, and finally associate them with the class via Shiftcls.

newisa.shiftcls = (uintptr_t)cls >> 3;Copy the code

This shows that ISA_T is the real structure of ISA.


3. Isa data structure

union isa_t {    
     isa_t() { }    
     isa_t(uintptr_t value) : bits(value) { }    

     Class cls;   
     uintptr_t bits;
#if defined(ISA_BITFIELD)    
     struct {        
        ISA_BITFIELD;  // defined in isa.h   
     };
#endif
};Copy the code

The underlying isa is ISA_T, and the structure of ISA_T is union + bitfield.

  • The size of the union depends on the size of the largest element inside, so isa is 8 bytes.
  • Elements in a union overwrite each other in memory, so CLS bits do not exist at the same time.

Look back, Nonpointer and! Nonpointer is binary, and Apple uses this mutual exclusion to define isa as a union. Apple, which pursues the ultimate optimization of memory, is obviously not satisfied with this, and has added a bit domain structure to make ISA all-inclusive.

  • Eight bytes are 64 binary bits. Each bit defines what is stored, called a bitfield.

Take a look at the contents of each bit defined by the ISA_BITFIELD macro (x86 architecture is used here, each architecture is slightly different, but the contents are the same, only some of the contents are stored at different lengths)

# define ISA_BITFIELD
\     uintptr_t nonpointer        : 1;                                     
\     uintptr_t has_assoc         : 1;                                       
\     uintptr_t has_cxx_dtor      : 1;                                       
\     uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ 
\     uintptr_t magic             : 6;                                      
\     uintptr_t weakly_referenced : 1;                                      
\     uintptr_t deallocating      : 1;                                      
\     uintptr_t has_sidetable_rc  : 1;                                      
\     uintptr_t extra_rc          : 8Copy the code


Describe the meanings of each memory bit:

nonpointer: indicates whether pointer optimization is enabled for isa Pointers (
0: pure ISA pointer, 1: not only class object address, ISA contains class information, object reference count, etc.)


has_assoc: Associated object flag bit (0 does not exist, 1 exists)


has_cxx_dtor: does the object have a destructor in C++ or Objc? If it has a destructor, it needs to do the destructor logic. If it does not, it can release the object faster



shiftcls: Stores the value of the class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers under the ARM64 architecture



magic
: used by the debugger to determine whether the current object is a real object or has no space to initialize


weakly_referenced
: whether the object is or was referred to an ARC weak variable,
Objects without weak references can be released faster.


deallocating: indicates whether the object is freeing memory



has_sidetable_rc: When the object reference technique is greater than 10, the variable is borrowed to store the carry



extra_rcWhen the reference count value of the object is actually reduced by 1,
For example, if the object’s reference count is 10, extra_rc is 9. If the reference count is greater than 10,
Use has_sideTABLE_rc below.



To verify this, initialize two objects

CJPerson *object = [CJPerson alloc];
NSLog(@"object = %p", object);        

CJPerson *object1 = [CJPerson alloc];                
objc_setAssociatedObject(object1, @"object1", object1, OBJC_ASSOCIATION_RETAIN_NONATOMIC);        
NSLog(@"object1 = %p", object1);Copy the code

Print out their respective ISA contents with LLDB



As you can see, the first bit is the same because it is both nonpointer. The only difference is that the second object is associated with the flag bit. In other places, there isa special shiftcls. As mentioned earlier, isa could fetch classes directly. Now it needs an isa_mask to fetch classes indirectly.

Take a look at object_getClass below. There isa code that does this, also using isa_mask to get the isa reference

return (Class)(isa.bits & ISA_MASK);Copy the code

The x86 isa_mask

# define ISA_MASK 0x00007ffffffffff8ULLCopy the code

Convert to binary

0000 0000 0000 0000 0111 1111 1111 1111 1111 1111 1111 1111 1111 1000Copy the code

This is obvious, starting with the third bit, the next 44 bits store the shiftCLs information, which corresponds to the isa stored content architecture for x86 given above. The isa & ISA_mask of the object will give the memory address of the object, that is, the reference to isa.



3. Isa bitmap

That’s according to AppleisaandsuperclassGo bitmap of.





As an example, print the bits of isa with the CJPerson object LLDB


x/4gxIt prints four consecutive 8-byte addresses of the object’s contents stored in memory, starting with the initial address.
x/5gx.
x/6gxAnd so on.



p/t
,
p/o
,
p/d
,
p/x
Represents binary, octal, decimal, and hexadecimal printing respectively.


Since isa is the first element in the object, the first address x/4gx prints is ISA, which is referred to isa by isa&isa_mask, and so on




Print the address in each Po




Conclusion:

  • Object’s ISA points to a class.
  • The isa of the class points to a metaclass of the same name, but at a different address.
  • The ISA of the metaclass points to the root metaclass.
  • The ISA of the root metaclass points to itself.


Conclusion:



isaIs an important clue to concatenate objects, classes, metaclasses and root metaclasses, using
Union adjoint fieldThe data structure makes full use of the limited space and stores rich information


The above is about the exploration of ISA, and the following will continue to update the underlying structure of the class, method forwarding, block, lock, multithreading and other low-level exploration, as well as application loading, startup optimization, memory optimization and other related knowledge points, please pay attention to.