One, foreword

We all know that every OC object contains an ISA pointer to its class object.

In the first section we introduced the basic alloc flow and introduced the _class_createInstanceFromZone function with this code:

As you can see, obj is simply a pointer to the allocated memory space through the malloc function. It is not yet bound to the class and cannot be called a true ISA. So how does it bind to the class afterwards?

The answer lies in initIsa. This method binds obj to the corresponding class to become the familiar ISA.

Explore initIsa()

Click on initIsa to see the implementation of the method:

As you can see, ISA is isa_t.

1, isa_t

Isa_t isa Union Commons with two members: bits and CLS. CLS is private and can only be accessed through get and set methods.

void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Copy the code

As we all know, values between members cannot exist at the same time, but can only exist independently and cover each other. That is, only one BITS and CLS can exist at the same time. CLS is easier to understand and can be used to set the class information corresponding to ISA.

What does bits do?

2, ISA_BITFIELD

Following the source code for initIsa, you can see this code:

struct {
    ISA_BITFIELD;  // defined in isa.h
};
Copy the code

You can guess that bits is used to support bit-field operations.

Click to see the definition of the parapet field in ISA_BITFIELD. The current Mac is Intel, so use __x86_64__ as an example:

Shiftcls: Stores the value of the class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers in the __arm64__ schema and 44 bits in __x86_64__.

Magic: Used by the debugger to determine whether the current object is a real object or has no space to initialize.

Weakly_referenced: Indicates whether an object is or has been 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 count is greater than 10, it needs to borrow this variable to store carry.

Extra_rc: The reference count of an object, which is the actual reference count minus 1. For example, if the object’s reference count is 10, extra_rc is 9. If the reference count is greater than 10, you need to use has_sideTABLE_rc above.

Bits is used to support bit-field operations, which store other information during the use of an object.

3, non pointer ISA

An ISA is more than just a pointer; it can also contain other information about how an object is being used. Why do you do that?

We know that every object hasisa, to take up8One byte of space. We print in binary formatp:

It turns out that there are a lot of 0’s in p, and they’re not being used. These locations can be used to store other things, such as reference counts, whether the object is being freed, and whether it points to a weak reference variable…… That’s right, that’s what Non Pointer Isa does.

Non pointer ISA: An ISA contains information that points to class objects as well as other information about the use procedure.

Raw Pointer ISA: ISA only contains information that points to class objects.

In the source code above, for raw Pointer ISA, we bind the class object directly to the obj pointer

newisa.setClass(cls, this);
Copy the code

For non pointer isa, it isa bit more complicated. In addition to setting class information, some other bit-field information is required:

#if SUPPORT_INDEXED_ISA watch ⌚️ Platform 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; #if ISA_HAS_CXX_DTOR_BIT newisa.has_cxx_dtor = hasCxxDtor; #endif newisa.setClass(cls, this); #endif newisa.extra_rc = 1;Copy the code

SUPPORT_INDEXED_ISA:

This macro has a value of 0 for both MacOS and iOS. I looked at the resources and found that this was only used under WatchOS. Supports storing classes in isa fields as indexes to class tables. It seems that there is a class table maintained by the system on Watch, and CLS all point to the offset position in the table.

I haven’t explored the WatchOS platform, so this is just for reference.

How did the pointer become isa

So how does class object information bind to a pointer? Focus on the setClass method:

shiftcls = (uintptr_t)newCls >> 3; Is the core method that stores the address of a class object into the Shiftcls bit field of ISA.

Here’s a tricky operation:

Why shift it three places to the right?

As we know from the definition of ISA_BITFIELD, the maximum addressing space MACH_VM_MAX_ADDRESS under __arm64__ is 0x1000000000, which is 36 bits in total. The maximum addressing space under __arm64__ MACH_VM_MAX_ADDRESS is 0x7ffFFFE00000, which is 47 bits.

But do we need to use all 36 and 47?

We know that the memory footprint of an object is 8-byte aligned, so the address of the object must be 8-byte aligned, and if it’s 8-byte aligned, the lower three digits of the address will be 0. And since they’re both 0’s, we don’t have to show them. This reduces the size of shiftcls by three bits.

By moving three Spaces to the right, the required memory space is reduced. It has to be said that Apple is quite particular about this piece of optimization 👏.

4. Derive class address from ISA

From the previous source code analysis, we saw how class objects are bound to ISA. Now, let’s reverse this and explore how to derive the address of a class from isa from the consumer’s point of view:

Look at the chestnut below 🌰 :

Address of Person class object: 0x00000001000085E0 Value of ISA pointer: 0x011D8001000085E1

Isa refers to a class object, so the value of Isa should be the same as the address of the Person class object, but the two printed values are clearly inconsistent. Why is that?

1. Obtain from ISA_MASK

The value of isa must be &isa_mask to get the true class object address.

#   define ISA_MASK        0x00007ffffffffff8ULL
Copy the code

I have to say that the translation of MASK into MASK is very, very appropriate 👍🏻. The nose mask reveals the nose, the eye mask reveals the eyes, and the ISA mask (ISA_MASK) reveals the ISA. Conversely, the translation of MASK into MASK, I do not know how many times obscure.

And that’s what we just gotPersonClass object addresses are identical.

2. Obtain by bit operation

Observe carefullyISA_BITFIELDIt can be found that,shiftclsIn front of3Bit, there’s a bit17position

We can restore the position of Shiftcls by doing a bit operation: first >>3 bits, remove the 3 bits on the right, then <<20 bits, remove the 17 bits on the left, and then >>17 bits. All that is left in isa is Shiftcls, which is the address of the class object.

Five, the summary

This section analyzes the principle of Non pointer ISA and how to derive the address of class object from ISA through source code. From now on, when it comes to ISA, we don’t just have to say “isa of instance objects points to class objects”, do we? 😊