Alloc and Init

The last book talked about OC objects and their alloc and init. Verify that the OC object isa structure at the bottom, and then in creatinstance, the last key method in alloc’s method call stack, there isa method that creates an isa pointer. In this article, we will talk about the ISA pointer first.

Why does everything inherit from NSObject?

We know that most of the objects in OC inherit from NSObject. (There’s only one NSProxy class THAT I know of that doesn’t inherit from NSObject, and that, like NSObject, is a base class.) Hold down command and jump into NSObject’s header file to see the following code:

@interface NSObject <NSObject> {
    #pragma clang diagnostic push 
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
}
Copy the code

So that’s the declaration of NSObject, it just has one ISA pointer, and everything inherits from NSObject, so it has its own ISA pointer. So what exactly does this ISA do? What the hell is it?

A domain

Before we get to ISA, let’s take a look at unions and bit-fields.

Consider how a class of attributes with four bool types should be stored and read. So the first thing we want to do is define it as a property. This is then done using the set and GET methods. Can we do this with a little bit of extra space, for example, a byte? The answer is yes, of course. In theory, a bool only needs one bit, and four bool only needs four bits. Talk is cheap,show me the code.

Insert the code that leads to bitfields and unions here

This, if further optimized, uses union to define this structure, which is the union we mentioned above. 6 don’t? If you use attributes, the object will take up a lot of memory, clearly 4 bits can be done, if you use attributes, let alone 4 bytes, 4*8 32 bits, is simply a waste, waste is shameful.

And then we have our hero, Isa. In the armV7 and ARMV7S era, apple did not optimize Pointers because it was a 32-bit operating system. Isa of instance objects pointed directly to class objects. In the arm64 architecture era, ISA takes up 8 bytes, a total of 64 bits. It would be too wasteful to store just one pointer. Therefore, Apple has made this 64 bit store a lot of attributes through federation and bit-field technology. Reduced memory overhead, reduced memory operations. Let’s look at the structure of ISA in detail.

The definition of the isa

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 indicates that isa.h is defined in isa.h
    };
#endif
};
Copy the code

(This definition is different for different architectures. If you want to verify the correctness of the mask, use the same mask as the architecture. All definitions are in the isa.h file of objC4-787.2.tar. gz source code.)

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;          // indicates that the attribute takes up one digit, starting with the lowest digit \
      uintptr_t has_assoc         : 1;          / / same as above \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)
Copy the code

Meaning of bits in isa Union union

  • Nonpointer Indicates whether pointer optimization is enabled for isa Pointers. The value 0 indicates pure ISA Pointers. 1 Indicates that ISA contains not only the address of the class object but also the class information and reference count of the object

  • Has_assoc: flag bit of associated object, 0 absent, 1 present (KVO implementation principle)

  • Has_cxx_dtor: does the object have a destructor for C++ or Objc? If it has a destructor, the destructor logic needs to be done. If not, the object can be freed faster

  • Shiftcls: Actually stores the value of the class pointer. With pointer optimization enabled, 33 bits of the ARM64 architecture are used to store Pointers to classes. This is why different architectures require different operations to read Pointers to classes. In 32-bit, isa points directly to class objects due to the fact that isa is not optimized, and after 64-bit, A pointer to the class object is obtained using an ISA pointer and an upper mask (ISA_MASK).

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

  • Weakly_refrenced: If the object is pointed to or has been pointed to an ARC weak variable, there is no weak reference to the object, which can be released faster

  • Dellocating: Indicates whether the object is freeing an object. Ing stands for continuous tense

  • Has_sidetable_rc: When the object reference count is greater than 0, it needs to borrow this variable to store carry. Weak is implemented using sidetables. We’ll talk more about this later when we talk about memory management.

  • Extra_rc: When representing the referential count value of this object, it is actually the referential count value minus 1. For example, if the object’s reference count is 10, extra_rc is 9. If the reference count is greater than 10, has_sideTABLE_rc needs to be used above.

Storing so much information in 64 bits is quite efficient!! When I first learned this, I was amazed! Apple has really taken this detail and this design to the extreme. The engineers at Apple are so damn talented, they all have amazing skills (or maybe I didn’t know that because I was in a hole).

Well, with ISA introduced, it’s time to move on to instance objects, class objects, and metaclass objects.

After the ARM64 architecture, ISA points to the class object via Shiftcls. The class object is also an object that has an ISA pointer, and its ISA shiftcls points to a metaclass object. Metaclass objects also have ISA, whose shiftcls all point to the metaclass object of the root class. There is a classic diagram on the web that shows the relationship between objects and metaclasses (this diagram will run through most of the blog posts on underlying principles) :

Diagrams of instance objects, class objects, and metaclass objects

So let’s put this into words

  • The isa of the instance object points to the class object, and the ISA of the class object points to the metaclass object. The ISA of a metaclass object points to the metaclass object of the base class (the ISA of the metaclass object of the base class points to itself). The shift_cls pointer, which is the actual pointer to the relationship, needs to be masked.
  • Class objects and metaclasses have superclass Pointers, superclass Pointers for ordinary class objects point to superclass objects of their parent class, and superclass Pointers for base class objects point to nil. The superclass pointer to the metaclass object of the base object points to the metaclass object of the parent class, and the superclass pointer to the superclass object of the parent class.

I don’t even know this class word after writing this. With this diagram in mind, you will have a good understanding of the relationship between instance objects, class objects, and metaclass objects. This picture is very nice and will be used later in the runtime message forwarding process and the relationship between these objects.

Why do we need class objects and metaclass objects

So why do we have class objects and metaclass objects? What’s in a class object and a metaclass object?

First, why. As we all know, we create classes that have a lot of properties, protocols, and methods, and methods are divided into instance methods and class methods. The implementation of these methods is uniform, and then call the process, but the value of the parameter is different, so these methods save a copy is enough, there is no need to save a copy in each object. So there are class objects and metaclass objects. If you think about the code you write for method calls, let’s say the Person class has an instance method called -(void)instanceFunction and a class method +(void)classFunction. This is what we call when we call:

- (void)callFunctions{
    [person instanceFunction];     // Use instance objects to call instance methods
    [Person classFunction];        // Call the class method with the class name (actually the class object)
}
Copy the code

You can clearly see the difference in method callers. This difference is made by class objects, metaclass objects, and ISA. Class object, which stores properties, protocols, and instance methods of the class. A metaclass object stores the class methods of this class. On method invocation, the instance method finds the object of the class through the ISA of the instance object, and then looks for the method in the class object. The class method finds the metaclass object of the class through the isa of the class object, and looks for the method in the metaclass object. This description, combined with the diagram above, gives you a good understanding of isa Pointers, class objects, and metaclass objects.

Let’s take a look at the actual implementation of class and metaclass objects and how they store properties, protocols, and methods in code.