Clang before we dive into the nature of the object

clang

ClangIs aC++Written and based onLLVMAnd published inLLVM BSDUnder licenseC/C++/Objective-C/Objective-C++The compiler.

clangTerminal compile command

Compile the object file into a c++ file

clang -rewrite-objc main.m -o main.cpp
Copy the code

Xcode is installed with the xcrun command, which is a bit more wrapped around clang to make it easier to use.

The simulator

xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
Copy the code

A:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main arm64.cpp
Copy the code

Nature of object

nature

Run the clang-rewrite-objc main.m -o main. CPP command to compile the main.m file.

#import <Foundation/ foundation.h > #import <objc/runtime.h> NSObject @property (nonatomic, strong) NSString *xhName; @property (nonatomic, assign) int xhAge; @end @implementation XHPerson @end int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Hello, World!" ); } return 0; }Copy the code

The XHPerson in the main.cpp file looks like this:

extern "C" unsigned long OBJC_IVAR_$_XHPerson$_xhName;
extern "C" unsigned long OBJC_IVAR_$_XHPerson$_xhAge;
struct XHPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	int _xhAge;
	NSString *_xhName;
};

typedef struct objc_object XHPerson;

struct NSObject_IMPL {
	Class isa;
};
Copy the code

Conclusion: XHPerson is the underlying structure, XHPerson_IMPL structure has three variables, _xhAge, _xhName are custom attributes, NSObject_IVARS is the ISA in NSObject. XHPerson inherits from NSObject, so XHPerson also has all of NSObject’s member variables.

The underlying NSObject is the NSObject_IMPL where only one member variable is Class ISA. It is usually called isa and is called an ISA pointer, so the Class should be a pointer type, searching for *Class globally in the main. CPP file. The following

typedef struct objc_class *Class; Struct objc_object {Class _Nonnull ISA __attribute__((deprecated)); }; Struct NSObject_IMPL {Class isa; };Copy the code
  • ClassThe type is actually oneobjc_classPointer to a structure of type,objc_classIs the underlying implementation of all classes. So we guessisaThere may be important associations with class information, and the specific associations will be explored below.
  • NSObjectWhat is the difference between the underlying implementation of object and the underlying implementation of object. The structure of both member variables isClass isa, then there must be inheritance relationship. At the bottom of all objects is inheritanceobjc_objectIn theOCAlmost all objects in theNSObject, but the real underlying implementation isobjc_objectStruct type of.

Object attribute low-level analysis

// getter
// getter
static NSString * _I_XHPerson_xhName(XHPerson * self, SEL _cmd) {
    return (*(NSString **)((char *)self + OBJC_IVAR_$_XHPerson$_xhName));
}
// setter
static void _I_XHPerson_setXhName_(XHPerson * self, SEL _cmd, NSString *xhName) {
    (*(NSString **)((char *)self + OBJC_IVAR_$_XHPerson$_xhName)) = xhName;
}
Copy the code

Member variables can be accessed by obtaining the first address of the object plus the offset address of the variable, and then accessing the memory according to the calculated address:

  • (char *)self: The first address of the object
  • OBJC_IVAR_$_XHPerson$_xhName: The offset address of the member variable name
  • *(NSString **)((char *)self + OBJC_IVAR_$_XHPerson$_xhName)Memory for the member variable name

A domain

/ / 4 * 8 = 32, 0000, 0000, 0000, 0000, 0000, 0000, 1111/0000 / four/three times / 1 byte waste struct XHCar1 {BOOL front; // 0 1 BOOL back; BOOL left; BOOL right; }; Struct XHCar2 {BOOL front: 1; BOOL back : 1; BOOL left : 1; BOOL right: 1; };Copy the code
  • The structure of the bodyXHCar1It takes 4 bytes because its internal member variables areBOOLType, in fact, can be put down in 1 byte.
  • The structure of the bodyXHCar2It takes 1 byte, saving 3 bytes.

A consortium

Struct XHTeacher1 {char *name; int age; double height ; }; // Union: mutually exclusive union XHTeacher2 {char *name; int age; double height ; };Copy the code

Teacher1 structure coexistence: Name, age, height all have values. Sizeof is 24

Teacher2 Mutual-exclusive: Only one of name, age, height has a value, the last one is assigned a value and the previous one is cleared. Sizeof is eight

  • Structure (struct), all variables are “co-existing” — the advantage is “tolerant”, comprehensive; The disadvantage is thatstructThe allocation of memory space is extensive, regardless of use, full allocation.
  • A consortium (union) is that the variables are mutually exclusive — the disadvantage is that they are not inclusive enough; But the advantage is that memory usage is more delicate and flexible, and also saves memory space.

nonPointerIsa

ISA is divided into pure ISA and NONPOINTER_ISA, which contains some information about the class in addition to a pure pointer.

The alloc underlying text will eventually bind the structure pointer from the heap to the current class via initIsa.

inline void 
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}
Copy the code
inline void objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor) { ASSERT(! isTaggedPointer()); isa_t newisa(0); if (! nonpointer) { newisa.setClass(cls, this); } else { ASSERT(! DisableNonpointerIsa); ASSERT(! cls->instancesRequireRawIsa()); #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 # if ISA_HAS_CXX_DTOR_BIT newisa.has_cxx_dtor = hasCxxDtor; # endif newisa.setClass(cls, this); #endif newisa.extra_rc = 1; } // 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 instantiation isa = newisa; }Copy the code
// uintptr_t () {} isa_t(uintptr_t value) : Bits (value) {} uintptr_t bits; private: // Accessing the class requires custom ptrauth operations, so // force clients to go through setClass/getClass by making this // private. Class cls; public: #if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // defined in isa.h }; bool isDeallocating() { return extra_rc == 0 && has_sidetable_rc == 0; } void setDeallocating() { extra_rc = 0; has_sidetable_rc = 0; } #endif void setClass(Class cls, objc_object *obj); Class getClass(bool authenticated); Class getDecodedClass(bool authenticated); };Copy the code

ISA_BITFIELD definition in ARM64 and X86_64 architecture

# if __arm64__ // ARM64 simulators have a larger address space, so use the ARM64e // scheme even when simulators build for ARM64-not-e. # if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR # define ISA_MASK 0x007ffffffffffff8ULL # define ISA_MAGIC_MASK 0x0000000000000001ULL # define ISA_MAGIC_VALUE 0x0000000000000001ULL # define ISA_HAS_CXX_DTOR_BIT 0 # define ISA_BITFIELD \ uintptr_t nonpointer : 1; \ uintptr_t has_assoc : 1; \ uintptr_t weakly_referenced : 1; \ uintptr_t shiftcls_and_sig : 52; \ uintptr_t has_sidetable_rc : 1; \ uintptr_t extra_rc : 8 # define RC_ONE (1ULL<<56) # define RC_HALF (1ULL<<7) # else # define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL # define ISA_HAS_CXX_DTOR_BIT 1 # 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 unused : 1; \ uintptr_t has_sidetable_rc : 1; \ uintptr_t extra_rc : 19 # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) # endif # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL # define ISA_MAGIC_MASK 0x001f800000000001ULL # define ISA_MAGIC_VALUE 0x001d800000000001ULL # define ISA_HAS_CXX_DTOR_BIT 1 # 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 weakly_referenced : 1; // uintptr_t unused: 1; \ // uintptr_t has_sidetable_rc : 1; // Uintptr_t extra_rc: 8 // Reference count # define RC_ONE (1ULL<<56) # define RC_HALF (1ULL<<7) # else # Error unknown architecture for Packed ISA # endif // SUPPORT_PACKED_ISA #endifCopy the code
  • nonpointer: indicates whether it is correctisaPointer Pointer optimization is enabled. 0: pure ISA pointer, 1: not only the address of the class object, ISA contains the class information, reference count of the object, and so on.
  • has_assoc: Flag bit of the associated object, 0 does not exist, 1 exists.
  • has_cxx_dtor: Indicates whether the object hasC++orObjcIf there is a destructor, it needs to do the destructor logic, if there is no, it can be faster to free the object.
  • shiftcls: Stores the value of the class pointer. Turn on pointer optimization in case ofarm64In the architecture33Bits are used to store class Pointers.
  • 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 the object is or has been pointed to oneARCObjects without weak references can be freed faster.
  • deallocating: indicates whether the object is freeing memory.
  • has_sidetable_rc: When the object reference technology is greater than10, the variable needs to be borrowed to store carry
  • extra_rcWhen representing the referential count value of the object, it is actually the referential count value minus1For example, if the object’s reference count is10, thenextra_rc9. If the reference count is greater than10, you need to use the followinghas_sidetable_rc.

Bit operations for ISA

throughISABit operation to getshiftcls. In order tox86_64For example,shiftclsin64Position in bit structure: there are on the right3Bits, on the left17position His contains44positionThe red box values are equal, indicating that the bitwise operation was successfulshiftcls.

conclusion

  • The underlying nature of an object is a structure.
  • ISAIs determined by the mutually exclusive nature of the Commons (commonwealth)ISAIs the pureISAorNONPOINTER_ISAAnd realize space saving by bit-field.