This article is fromThe public account of RiverLi

Objective-c dynamics

We all know OC is a dynamic language, so how do we understand dynamic? Dynamic is the ability to defer action to runtime, which is the state of execution after the code has been compiled and linked. The dynamics of the OC is made possible by the Runtime. Runtime is a C API that encapsulates many dynamically-related functions. Dynamic has many practical applications, such as:

  • Add attributes to categories using AssociatedObject
  • Iterate over all member variables of the class.
  • Exchange method implementation.
  • Use the message forwarding mechanism to resolve exceptions where methods cannot be found
  • , etc.

Isa,

We know that OC has the concept of class and metaclass. The following figure is a classical object relation diagram, which can be seen from the figure:

  1. For an object isa refers to its class.
  2. For a class object isa refers to its metaclass.

Let’s look inside the object by reading the source code.

  1. First, in the nsobject. h file we can see the following information
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
Copy the code
  1. We use main.m to compile OC source code into CPP code using the Clang tool
// Compile the command
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

//main.m
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
    }
    return 0;
}
Copy the code

By looking at the main-arm64.cpp file we can find the following information:

/ / 1.
typedef struct objc_object NSObject; / / 2.struct NSObject_IMPL {
	Class isa;
};
Copy the code
  1. By reading the Runtime source code, we can obtain the following information
//objc-runtime-new.h
struct objc_class : objc_object {
    Classsuperclass; . }// objc-private.h
typedef struct objc_class *Class;
typedef struct objc_object *id;

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

We can draw the following conclusions:

  • NSObject is actually a struct objc_Object.
  • Objc_class inherits from objc_Object.
  • Class is actually a struct objc_class type.
  • Objc_object contains an internal isa variable of type ISA_t.

Let’s move on to the implementation of ISA as follows:

// objc-private.h
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
};

// isa.h
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      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)

//objc-class.mm
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

//objc-object.h
inline Class 
objc_object::getIsa() 
{
    if(! isTaggedPointer())return ISA(a); uintptr_t ptr = (uintptr_t)this;if (isExtTaggedPointer()) {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        return objc_tag_ext_classes[slot];
    } else {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        returnobjc_tag_classes[slot]; }}//objc-object.h
#if SUPPORT_NONPOINTER_ISA
inline Class 
objc_object::ISA() 
{
    assert(! isTaggedPointer()); #if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}
// SUPPORT_NONPOINTER_ISA
#else
// not SUPPORT_NONPOINTER_ISA


inline Class 
objc_object::ISA() 
{
    assert(! isTaggedPointer());return isa.cls;
}
Copy the code

Isa is implemented by the UNION structure. The SUPPORT_NONPOINTER_ISA macro is used to specify different fetching methods when fetching ISA contents. Let’s look at the definition of macros

#if !SUPPORT_INDEXED_ISA&&!SUPPORT_PACKED_ISA
#   define SUPPORT_NONPOINTER_ISA 0
#else
#   define SUPPORT_NONPOINTER_ISA 1
#endif

#if __ARM_ARCH_7K__ >= 2|| (__arm64__ && ! __LP64__) # defineSUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif

#if(! __LP64__ ||TARGET_OS_WIN32| | \ [TARGET_OS_SIMULATOR&&!TARGET_OS_IOSMAC))
#   define SUPPORT_PACKED_ISA 0
#else
#   define SUPPORT_PACKED_ISA 1
#endif
Copy the code

SUPPORT_INDEXED_ISA=0, SUPPORT_PACKED_ISA=0 SUPPORT_NONPOINTER_ISA = 0, SUPPORT_INDEXED_ISA = 0. So the return value of object_getClass is a.bit&isa_mask ‘

Meaning of other fields in ISA

  • Nonpointer: 0: a common pointer that stores the memory address of a Class or meta-class object. 1, represents optimized, using bitfields to store more information
  • Has_assoc: is the associated object set? If not, it will be released faster
  • Has_cxx_dtor: does C++ destructor (.cxx_destruct) exist, if not, it will be released faster
  • Shiftcls: Stores the memory address information of Class and meta-class objects
  • Magic: Used during debugging to tell if an object has not been initialized
  • Weakly_referenced: Whether a weak reference has been pointed to, if not, the release will be faster
  • Deallocating: Whether the object is being released
  • Extra_rc: Stores values in the reference counter minus 1
  • Has_sidetable_rc: Whether the reference counter is too large to be stored in ISA. If it is 1, then the reference count is stored in an attribute of a class called SideTable.

conclusion

  1. The ISA of an object points to a class, the ISA pointer of a class points to a metaclass, and the ISA pointer of a metaclass points to a base class.
  2. Isa is represented by a union structure. The address fetching method for isa varies according to platforms. For example, in the ARM64 environment, the real address is ISa.bits & ISA_MASK

reference

Ref. 1 Ref. 2 Ref. 3: Common body