preface

If you want to be an iOS developer, you have to read source code. The following is a small series that the author combed in OC source code exploration — Class and Object, welcome you to read and correct, but also hope to help you.

  1. Object creation for OC source analysis
  2. OC source code analysis of ISA
  3. OC source code analysis and such structural interpretation
  4. Caching principle of OC source analysis method
  5. OC source code analysis method search principle
  6. Analysis and forwarding principle of OC source code analysis method

1. isaintroduce

1.1 isaWhat is the

In the OC source code analysis object creation article, we know that alloc will call calloc to allocate memory, followed by initInstanceIsa(CLS, hasCxxDtor), as the name means initialization object ISA, its key code is as follows

inline void 
objc_object::initInstanceIsa(Class cls, boolhasCxxDtor) { assert(! cls->instancesRequireRawIsa()); assert(hasCxxDtor == cls->hasCxxDtor());// Pay attention to true here
    initIsa(cls, true, hasCxxDtor);
}

inline void 
objc_object::initIsa(Class cls, bool nonpointer, boolhasCxxDtor) { 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;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        / /... but not too atomic because we don't want to hurt instantiationisa = newisa; }}Copy the code
  1. aboutTagged Pointer

It is said that Apple introduced Tagged Pointer to save memory and improve execution efficiency. For 64-bit applications, the introduction of Tagged Pointer reduces memory usage by half, increases access speed by 3 times, and increases creation and destruction speed by 100 times.

Tagged Pointer was first used on the iPhone 5S and is now used almost everywhere. Tagged Pointer Tagged Pointer Tagged Pointer Tagged Pointer Tagged Pointer

  1. isa_ttype

Click isa in objc_object::initIsa() to see that ISA is isa_T

struct objc_object {
private:
    isa_tisa; .// Some public and private methods
};
Copy the code

And isa_T is actually a union.

So let’s just generalize the distinction between structs and unions

  1. Both can contain multiple different types of data, such asint,double,ClassAnd so on.
  2. instructEach member has its own memory spacestructThe total memory length of a variable is greater than or equal to the sum of the memory lengths of each member. And in theunionIn, each member shares a memory space, oneunionThe total memory length of a variable is equal to the memory length of the member with the longest memory.
  3. rightstructThe values of the members in the. rightunionOnly one member can be assigned at a time, and the values of other members will not exist.

Isa_t contains two member variables CLS and BITS, which are structured as follows

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

1.2 isathebitsMember variables

  1. A domain

Let me introduce you to the concept of bitfields

A bit field is a data structure that stores data in compact bits and allows programmers to manipulate the bits of the structure.

  • Advantages:
    • Save storage space;
    • Easy access to parts of an integer value can simplify the program’s source code.
  • Disadvantages:
    • The way its memory allocation and alignment are implemented depends on the specific machine and system, and may have different results on different platforms, resulting in bitfields being inherently non-portable.

The bits member variable of ISA is of type uintptr_t, which is essentially an unsigned long

typedef unsigned long           uintptr_t;
Copy the code

In 64-bit CPU architecture, bits are 64 bits, or 8 bytes, and their individual bits are stored in a bit field, ISA_BITFIELD.

  1. ISA_BITFIELD

Next, take a look at the source code of ISA_BITFIELD (since THE author is using macOS project to study OC underlying source code, so here x86_64 architecture as an example)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   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          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)
Copy the code

To be clear, the ISA pointer is also 8 bytes long on a 64-bit CPU architecture, which can store a lot of content. Apple uses only part of the bits (44 under X86_64, 33 under ARM64) to store the class address for optimal performance, with the rest of the bits used to store some other information.

Specific analysis of the meaning of each member of the ISA_BITFIELD bit field:

  • nonpointer: indicates whether it is correctIsa pointerEnable pointer optimization.
    • 0: not optimized, pureIsa pointerWhen the accessisaPointer directly returns its member variablecls
    • 1: optimization, i.eIsa pointerThe content not only contains the class address, but also contains some information about the class, reference count of the object, and so on.
  • has_assoc: Indicates whether there is an associated object.
  • has_cxx_dtor: whether the object has a destructor for C++ or Objc.
    • If there is a destructor, you need to do some logical processing of the destructor.
    • If not, the object can be released more quickly.
  • shiftcls: Storage class address. Turn on pointer optimization in case ofx86_64Architecture has44Used to store class addresses,arm64In the architecture33
  • magic: used by the debugger to determine whether the current object is a real object or an uninitialized space.
  • weakly_referenced: Indicates whether an object is or has been pointed to oneARCObjects without weak references are freed faster.
  • deallocating: Indicates whether the object is freeing memory.
  • has_sidetable_rc: Whether the reference count value of the object is carried.
  • extra_rc: represents the referential count value of the object.extra_rcJust store the extra reference count, the actual reference count formula:Actual reference count = EXTRA_RC + 1. This takes up 8 bits, so the maximum reference count you can theoretically store is:2^8-1 + 1 = 256(arm64CPU architectureextra_rcWith 19 bits, the maximum reference count that can be stored is2^19 - 1 + 1 = 524288).
    • withhas_sidetable_rcWhen the maximum reference count of an object exceeds the threshold,has_sidetable_rcIs 1, or 0 otherwise

1.3 isatheclsMember variables

After analyzing isa’s bitfields, only CLS is left, which is of Class type and also source code

typedef struct objc_class *Class;
// let's take a look at the type of id. Obviously id isa pointer variable, and its value isa single isa variable
typedef struct objc_object *id;

struct objc_class : objc_object {
    // Class ISA; 
    Class superclass;
    cache_t cache;             
    class_data_bits_t bits;

    class_rw_t *data() { 
        returnbits.data(); }...// Some methods
};

struct objc_object {
private:
    isa_tisa; .// Some public and private methods
};

Copy the code

Class isa pointer to the objc_class structure, which in turn inherits from objc_object. Therefore, the value of the Class pointer has an isa member variable (isa_t). This ISA member variable is 8 bytes on a 64-bit CPU architecture and ranks in the first 8 bytes of the objC_class structure.

1.4 isaThe role of

As can be seen from the objC_Object structure, when the system allocates memory for an object and initializes the instance variables, the first object in the structure of the instance variables of these objects is ISA.

At the same time, we know that Shiftcls stores class addresses through the bitfield specification for ISA. In the X86_64 architecture, Shiftcls occupies bit 44, or bit 3, 46. Fill all the [3, 46] bits with 1, [0, 2] bits and [47, 63] bits with 0 to get 0x7FFFFFFFFFF8, which is the value of ISA_MASK. Therefore, ISA & ISA_MASK will get the class address stored by Shiftcls. That’s what masks are for.

As shown in the figure below

Here is an example of what ISA can do

In this case, run the LLDB command to debug

Description:

  1. 0x001d800100001129isObject ptheisaValue, throughisa & ISA_MASKcomputed0x0000000100001128isPersonThe address of the class
  2. Proof [1] : Passp/x Person.classPrint directlyPersonClass address, of course0x0000000100001128, so [1] prove true!

Conclusion:isaAssociating objects with classes acts as a bridge between them.

Note: The analysis of Shiftcls is based on nonpointer being 1. If nonpointer is 0, the entire ISA stores CLS.

Consider: if you don’t use ISA_MASK, how do you justify isa for this purpose? Answers will be added at the end of this article.

1.5 isaInitialization supplement of

A final note on isa initialization. Remember the entry to initialize ISA? Is initIsa (CLS, true, hasCxxDtor); The value of nonpointer is true and SUPPORT_INDEXED_ISA is defined

#if__ARM_ARCH_7K__ >= 2 || (__arm64__ && ! __LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif
Copy the code

Under x86_64, SUPPORT_INDEXED_ISA is 0, so the initialization of ISA will eventually come

isa_t newisa(0);

// Assign the value to bits using ISA_MAGIC_VALUE(0x001d800000000001ULL)
// Nonpointer is 1, magic is 1d, and other variables are zero
newisa.bits = ISA_MAGIC_VALUE;
// hasCxxDtor is fetched from class ISA
newisa.has_cxx_dtor = hasCxxDtor;
// shiftcls 3 bits to the right and assign to shiftcls
newisa.shiftcls = (uintptr_t)cls >> 3;

isa = newisa;
Copy the code

I won’t go into hasCxxDtor here because of space.

2 isaPointing to the figure

Through the above source code analysis, we realize that the isa pointer to an object points to the class to which the object belongs. The class itself has an ISA pointer. What does it point to?

This is where the concept of meta Class comes in. Let’s take a look at metaclasses first

In OC, methods of objects are not stored in the structure of objects (if each object holds methods that it can execute, this can have a significant impact on memory usage).

When an instance method of an object is called, it looks up the corresponding class through its own ISA and then looks up the implementation of the corresponding method in the class_datA_bits_t structure of the owning class. Also, each objC_class has a superclass pointer to its superclass to look up inherited methods.

What happens when a class method is called? The solution to this OC is to introduce metaclasses to ensure that class methods can be found through the same mechanism. That is, the ISA of a class points to a metaclass.

Apple officially has an ISA orientation chart, i.e

So let’s verify that.

2.1 Preparations

Create the Teacher class, where the Person class inherits from NSObject and the Teacher class inherits from the Person class.

Teacher equals Subclass, Person equals Superclass, and NSObject equals Root class.

2.2 Verification Process

  1. To obtainteacherObject class (the result isTeacherClass at the address0x0000000100001230)
(lldb) x/4gx teacher
0x100f59400: 0x001d800100001231 0x0000000000000000
0x100f59410: 0x636f72504b575b2d 0x70756f7247737365
(lldb) p/x 0x001d800100001231 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $3 = 0x0000000100001230      // Class address of teacher object
(lldb) po $3
Teacher     // Class object teacher
Copy the code
  1. To obtainTeacherClass metaclass (the result isThe Teacher metaclass, the address is0x0000000100001208)
(lldb) x/4gx Teacher.class
0x100001230: 0x001d800100001209 0x00000001000011e0
0x100001240: 0x0000000100f61150 0x0000000100000003
(lldb) p/x 0x001d800100001209 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $5 = 0x0000000100001208      // Teacher class metaclass address
(lldb) po $5
Teacher     // Teacher class metaclass
Copy the code

The Teacher class and the Teacher metaclass address are different

  1. To obtainpersonObject class (the result isPersonClass at the address0x00000001000011e0), and the metaclass of the class (the result isThe Person metaclass, the address is0x00000001000011b8)
(lldb) x/4gx person
0x100f60a30: 0x001d8001000011e1 0x0000000000000000
0x100f60a40: 0x0000000000000002 0x00007fff9b855588
(lldb) p/x 0x001d8001000011e1 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $8 = 0x00000001000011e0      The class address of the person object
(lldb) po $8
Person      // The class of the object Person

(lldb) x/4gx Person.class
0x1000011e0: 0x001d8001000011b9 0x0000000100b38140
0x1000011f0: 0x0000000100f61030 0x0000000100000003
(lldb) p/x 0x001d8001000011b9 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $10 = 0x00000001000011b8     // Metaclass address of the Person class
(lldb) po $10
Person      // Metaclass of the Person class
Copy the code
  1. To obtainobjectObject class (the result isNSObjectClass at the address0x0000000100b38140), and the metaclass of the class (the result isNSObject metaclass, the address is0x0000000100b380f0)
(lldb) x/4gx object
0x100f5cc50: 0x001d800100b38141 0x0000000000000000
0x100f5cc60: 0x70736e494b575b2d 0x574b57726f746365
(lldb) p/x 0x001d800100b38141 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $12 = 0x0000000100b38140     // The class address of object
(lldb) po $12   
NSObject    // The class of object

(lldb) x/4gx NSObject.class
0x100b38140: 0x001d800100b380f1 0x0000000000000000
0x100b38150: 0x0000000101913060 0x0000000200000003
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $14 = 0x0000000100b380f0     // The metaclass address of NSObject
(lldb) po $14
NSObject    // the NSObject metaclass
Copy the code
  1. To obtainThe Teacher metaclassThe metaclass,The Person metaclassThe metaclass, as well asNSObject metaclassThe metaclass
(lldb) x/4gx 0x0000000100001208     / / the Teacher metaclass
0x100001208: 0x001d800100b380f1 0x00000001000011b8
0x100001218: 0x000000010186f950 0x0000000400000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $16 = 0x0000000100b380f0     / / NSObject metaclass
(lldb) po $16
NSObject    / / NSObject metaclass

(lldb) x/4gx 0x00000001000011b8     / / Person metaclass
0x1000011b8: 0x001d800100b380f1 0x0000000100b380f0
0x1000011c8: 0x0000000101905a50 0x0000000300000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $17 = 0x0000000100b380f0     / / NSObject metaclass
(lldb) po $17
NSObject    / / NSObject metaclass

(lldb) x/4gx 0x0000000100b380f0     / / NSObject metaclass
0x100b380f0: 0x001d800100b380f1 0x0000000100b38140
0x100b38100: 0x0000000101903820 0x0000000500000007
(lldb) p/x 0x001d800100b380f1 & 0x00007ffffffffff8  // isa & ISA_MASK
(long) $18 = 0x0000000100b380f0     / / NSObject metaclass
(lldb) po $18
NSObject    / / NSObject metaclass
Copy the code

2.3 isaPoint to the conclusion

Based on the verification process of [2.2], it can be concluded that:

  • The object’sIsa pointerRefers to the class to which the object belongs (e.gpersonThe object’sisaPoint to thePersonClass)
  • Of the classIsa pointerA metaclass pointing to a class (e.gPersonOf the classisaPoint to theThe Person metaclass)
  • metaclassIsa pointerPoints to the root metaclass (e.gThe Person metaclasstheisaPoint to theNSObject metaclass)
    • NSObject classClass is the yuanA metaclass
    • NSObject metaclasstheIsa pointerPointing to itself (it’s a circle)

Consider: If the Person class inherits from NSProxy, what does the isa reference look like? Those who are interested can try it.

2.4 Proof of inheritance relationship

Class inheritance relationship proof process :(use -> to indicate inheritance from)

(LLDB) p class_getSuperclass(teacher.class) (class) $19 = Person // Teacher -> Person (LLDB) p Class_getSuperclass (person.class) (class) $20 = NSObject // Person class -> NSObject (LLDB) p Class_getSuperclass (NSObject. Class) (class) $21 = nil // NSObject class -> nilCopy the code

Proof of inheritance relation of metaclass :(with -> to indicate inheritance from)

// 0x0000000100001208 is Teacher metaclass (LLDB) p/x class_getSuperclass((Class)0x0000000100001208) (Class) $17 = 0x00000001000011B8 // Person metaclass (LLDB) Po $17 Person // Teacher metaclass -> Person metaclass // 0x00000001000011B8 is Person metaclass (LLDB) P /x Class_getSuperclass ((Class) 0x00000001000011B8) (Class) $22 = 0x0000000100B380F0 // NSObject meta-class (root meta-class) (LLDB) Po $22 NSObject Person metaclass -> root metaclass // 0x0000000100B380F0 is the root metaclass (LLDB) p/x class_getSuperclass((Class) 0x0000000100b380F0) (Class) $23 = 0x0000000100B38140 NSObject Class (root Class) (LLDB) Po $23 NSObject root metaclass -> root ClassCopy the code

The root metaclass inherits from the root class (NSObject metaclass -> NSObject class) and the root class inherits from nil (NSObject class -> nil)

2.5 isaPoint to the doodle version of the diagram

Apply the above example to the ISA pointing diagram to get the following image

3. Summary

  1. isaisisa_tStructure, adoptedUnion + bit-fieldDisplay different content in different bits to save storage space and optimize memory.
  2. isaContains theclsandbitsTwo member variables, the two member variables inA 64 - bitCPU architecture is always 8 bytes long, soisainA 64 - bitThe length is also 8 bytes in CPU architecture.
  3. isaThe bitfield stores some object and class information, and associates the object and class, plays the role of intermediate bridge.
  4. isaConclusions related to the pointing graph:
    • The object’sIsa pointerRefers to the class to which the object belongs (e.gpersonThe object’sisaPoint to thePersonClass)
    • Of the classIsa pointerA metaclass pointing to a class (e.gPersonOf the classisaPoint to theThe Person metaclass)
    • metaclassIsa pointerPoints to the root metaclass (e.gThe Person metaclasstheisaPoint to theNSObject metaclass)
      • A metaclasstheIsa pointerPointing to itself (it’s a circle)
    • Inheritance relationships of metaclasses are passed upward (e.gThe Teacher metaclassInherited fromThe Person metaclass)
      • A metaclassInherited fromThe root class
      • The root classInherited fromnil

Added 4.

4.1 isaProof of the effect of 2

Q: If you don’t use ISA_MASK, how do you prove that ISA relates the role of objects and classes?

A: Shiftcls is 44 bits long in x86_64 and stored in [3, 46] bits of ISA. Therefore, by clearing [0, 2] bits and [47, 63] bits of ISA, shiftcls value can also be obtained to determine the class.

As shown in the figure, an operation on ISA successfully yields the same address as the Person class.

4.2 NSProxytheisaPoint to the

Q: If the Person class inherits from NSProxy, what does the isa reference look like?

A: Like NSObject, both are root classes.

The resources

Learning about ISA from NSObject Initialization (by DraP)

PS

  • The source code project has been placedgithubThe stamp, pleaseObjc4-756.2 – the source code
  • You can also download apple’s official ObjC4 source code to study.
  • Reprint please indicate the source! Thank you very much!