Objective-c (hereinafter referred to as OC) is a highly dynamic programming language. The dynamic of OC is realized based on Runtime. Runtime system is written by C++ assembly language, and the API provided by OC is basically in C language. Here we explore the nature of the class from the Runtime code provided by Apple.

Runtime source code

Legacy version

The OC Runtime is divided into two versions. One is legacy and one is Modern. Many readers have seen the following code representing the OC class structure:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if! __OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
Copy the code

The legacy version was deprecated after the Objective-C 2.0 release at WWDC 2006, and the contents of the OBJC2_UNAVAILABLE flag are no longer used. What is the current structure?

object

In OC, each object is an instance of a class.

struct objc_object {
private:
    isa_t isa;
    // ...
}
Copy the code

There is only one isa member variable in the structure representing the object. Under the ARM64 architecture, isa is optimized to store not only the address information, but also other information. So the essence of an object isa structure that contains a private member variable isa, and isa holds an address that points to the class to which the object belongs. Different objects have different member variables, and after compilation, the structure of each object also stores its own value of the member variable.

Get the compiled code xcrun-sdk iphoneOS clang-arch arm64-rewrite-objc Coder. M

@interface Coder : Person @property (nonatomic, copy) NSString *name; Struct NSObject_IMPL {Class isa; }; struct Coder_IMPL { struct NSObject_IMPL NSObject_IVARS; NSString * _Nonnull _name; };Copy the code

It is also easy to understand why the value of a member variable exists in an object. Each object must exist independently and must have its own variable value. Where do variable names and methods and so on go? Classes!

class

Class holds the type of member variable, method, etc., source code is as follows:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    / / to omit...
}
Copy the code

The first thing you can see is that objc_class inherits objC_Object, so classes in OC can actually be understood as objects, called class objects. In legacy versions, there is only one ISA pointer in the structure of an object that points to its class object. The class object also has an ISA pointer to its metaclass. After the Modern version uses inheritance, the structure of the class object inherits the optimized ISA variable. However, a comparison of the two versions shows that the modern version is missing many of the variables except superclass&cache and has a bits variable.

struct class_data_bits_t {
    uintptr_t bits;
    
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
	// ...
}
Copy the code

Inside this structure is a pointer to class_rw_t, which is obtained by a bit operation. Visible bits hold Pointers to class_rw_T and other information. Then turn your eyes to class_rw_t:

‘rw’ and ‘ro’ stand for ‘readwrite’ and ‘readonly’ respectively

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    // ...
}
Copy the code

As you can see, the list of methods, properties, and protocols from legacy is in this directory. These lists can be thought of as two-dimensional arrays, readable and writable, containing the initial contents of the class and the contents of the classification. Two-dimensional arrays are easy to add. And here we have class_ro_t:

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    / /...
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    / /...
};
Copy the code

BaseMethodList, baseProtocols, Ivars, and baseProperties in class_ro_t can be understood as one-dimensional arrays that are read-only and contain the initial contents of the class.

From this we can also see why classes cannot dynamically add member variables to class objects. Classes are loaded by the Runtime, the class structure has already been determined, and the memory where member variables are stored is read-only.

The metaclass

As mentioned above, the address stored in the ISA of a class object refers to the class of an even class object, called a metaclass, which holds the object’s methods. That is, instance methods are stored in a class, and class methods are stored in a metaclass. Use a classical diagram to represent the relationship between objects, classes, and metaclasses.

The relationship between the three is well illustrated in the figure, but two points need to be emphasized here.

  • metaclassisaRefers to the metaclass of the base class.
  • Metaclass of the base classsuperclassIt points to the base class

These two points are easy to miss and often appear in some interview questions.

reference

Objective-c object model