(Summary of articles on underlying principles of iOS)

(iOS)

The main purpose of this article is to understand how classes relate to ISA

Before introducing the body, you first need to understand a concept: What is the essence of an OC object?

OC Object Essence

Before you explore the nature of the OC object, look at a compiler: clang

Clang

  • clangIs aAppleLead writing, based onLLVMtheC/C++/OC compiler
  • Mainly used forThe underlying compilerTo someFile ' 'outputintoC + + files, e.g.main.mThe output intomain.cppFor better observationThe underlyingSome of thestructure 及 implementationLogic, easy to understand the underlying principles.

Explore the nature of objects

  • inmainTo customize a class inLGPerson, has an attribute name
@interface LGPerson : NSObject @property (nonatomic, copy) NSString *name; @end @implementation LGPerson @end copy the codeCopy the code
  • Through the terminal, usingclangwillmain.m Compiled intomain.cppThere are several compile commands, the first of which is used here
CPP clang -rewrite-objc main.m -o main.cpp //2. Compile viewController.m into viewController.cpp clang -rewrite-objc-fobjc-arc-fobjc-Runtime = ios-13.0.0-isysroot / / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator13.7. The SDK Viewcontroller. m // The following two ways are through the command line specifying the schema mode, -xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp //4, iphoneos clang-arch arm64-rewrite-objc main.m -o main-arm64.cppCopy the code
  • Open the compiled main. CPP and find the LGPerson definition. LGPerson will be compiled into struct structures

    • LGPerson_IMPLThe first property in theisaIs inherited fromNSObject, belong toPseudo inheritance, pseudo-inheritedwayIs directly to theNSObjectThe structure is defined asLGPersonIn theThe first attributeThat means thatLGPersonhaveNSObjectIn theAll member variables.
    • LGPersonThe first property inNSObject_IVARSEquivalent toNSObjectIn theisa
@interface NSObject <NSObject> {Class ISA OBJC_ISA_AVAILABILITY; } struct NSObject_IMPL {Class isa; }; Struct LGPerson_IMPL {struct NSObject_IMPL NSObject_IVARS; // Equivalent to Class isa; NSString *_name; }; Copy the codeCopy the code

See the figure belowThrough the above analysis, we understand the nature of the OC object, but seeNSObject“, which raises the question: whyisaIs of typeClass?

  • inIOS – Underlying principles 02: Alloc & Init & New source code analysisIt’s mentioned in the articleallocOne of the core methodsinitInstanceIsaMethod, by looking at the source implementation of this method, we find that the initializationisaPointer is passedisa_tType initialized,
  • And in theNSObjectThe type of ISA in the definition isClassThe fundamental reason is thatisaThe external feedback isThe class informationIn order to make developers moreclearThat need to be inisaI did one on the returnType cast, similar to theswiftIn theasThe strong. In the sourceisatheStrong goSee the figure below

conclusion

Therefore, it can be concluded from the above exploration process:

  • The nature of the OC objectIn fact, isThe structure of the body
  • LGPersonIn theisaIs inherited fromNSObjectIn theisa

Objc_setProperty source code exploration

In addition toLGPersongWe find that there are also propertiesnameThe correspondingsetgetMethod, as shown in the figure below, wheresetMethod implementation depends on runtimeobjc_setProperty.

You can take the following steps to unravel the underlying implementation of objc_setProperty

  • Search globally in objC4-781objc_setPropertyTo findobjc_setPropertySource code implementation of

  • Enter thereallySetPropertySource code implementation, the principle of the method isThe new value is retain, the old value is release

conclusion

By exploring the underlying source code of objc_setProperty, there are the following points:

  • objc_setPropertyThe purpose of the method is to applyCorrelation of the upperthesetMethods andThe underlyingthesetMethod, its essence is ainterface
  • It’s designed this waywhyIs that,The upperthesetThere’s a lot of ways to do that ifCall the underlying set directlyMethod, there will be a lot ofTemporary variableWhen you want toTo find theFor an SEL, it’s going to be verytrouble
  • For these reasons, Apple adoptedAdapter design pattern (adapting the underlying interface to the interface required by the client).foreignTo provide ainterfaceFor the upper set method,internalCall the underlyingSet method, so that they are not affected by each other, i.eNo matter how the top changes, the bottom stays the same, rightOr,Changes at the bottom do not affect those at the topTo isolate the upper and lower interfaces

Below is the relationship between the top layer, the isolation layer, and the bottom layer

How CLS relates to classes

After analyzing the first two of the three alloc cores in ios-Underlying Principles 02: Alloc & Init & New and ios-underlying principles 05: Memory Alignments, today explore how initInstanceIsa connects CLS to ISA

Before we do that, we need to understand what isa union and why is the isa type isa_t defined using a union

Union

There are two ways to construct data types:

  • The structure of the body(struct)
  • A consortium(union, also known asThe appropriate)

The structure of the body

A structure is a grouping of disparate data into a whole whose variables co-exist and which allocates memory whether or not the variables are used.

  • Disadvantages: All attributes allocate memory, compareWaste of memorySuppose there are four int members allocated16Bytes of memory, but in use, you only use4Bytes, the rest12Bytes are a waste of memory
  • Advantages: StorageCapacity is larger.receptive, and between membersThey don't interact

A consortium

Unions are also composed of different data types, but their variables are mutually exclusive, and all members occupy a memory. In addition, the shared body adopts memory override technology, which can only store the value of one member at a time. If a value is assigned to a new member, the value of the original member will be overwritten

  • Disadvantages: Weak inclusiveness
  • Advantages: All members share the same memory, which makes memory use more flexible and saves memory space

The difference

  • Memory usage

    • Structure of theEach member consumes different amounts of memoryWith each otherHave no effect on
    • Share bodyAll members occupy the same memory segment, modifying a member willimpactAll the other members
  • Memory allocation size

    • Structure in vivo> =Occupied by all membersThe sum of memory(There may be gaps between members)
    • The appropriateThe amount ofmemoryIs equal to theLargest memberThe amount ofmemory

The type of ISA is ISA_t

The following is the definition of the isa pointer type isa_t, which is defined by union.

Union isa_t {// uintptr_t value) : bits(value) {} uintptr_t bits; #if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // defined in isa.h }; #endif }; Copy the codeCopy the code

The reason the ISA_T type uses unions is also based on memory optimization, which is implemented in the ISA pointer through char + bit fields (that is, each bit in the binary can represent different information). In general, the memory size of the ISA pointer is 8 bytes, or 64 bits, which is enough to store a lot of information, which can greatly save memory and improve performance

From the definition of isa_t we can see that:

  • We provide two members, CLS and bits, which are mutually exclusive as far as the union definition is concerned, which means that there are two ways to initialize an ISA pointer

    • throughclsInitialization,Bits There is no default value
    • throughbitsInitialization,CLS has a default value
  • The structure member ISA_BITFIELD, which is a macro definition, has two versions: __arm64__ (for ios mobile) and __x86_64__ (for macOS). Here are some of their macro definitions. See the figure below

nonpointerThere are two values, representing the custom class, etc1A –0:Pure isa pointer1: not onlyClass object address, is included in the ISAThe class informationAnd the object’sReference countingEtc.

- ` has_assoc ` said ` associations sign `, accounts for ` ` one position - ` 0 ` : ` unrelated ` object - ` ` 1: 'there is an associated' object - 'has_cxx_dtor' indicates whether the object has a C++/OC 'destructor' (similar to 'dealloc'), occupying '1' bit - if 'has a' destructor ', then 'needs to do' destructor 'logic - if' does not ', 'shiftcls' means' store the value of the pointer to the class' (the address of the class), that is, the class information - 'arm64' takes' 33 'bits, when pointer optimization is enabled, In the ARM64 architecture there are '33' bits to store Pointers to classes - '44' bits in 'x86_64' - and 'magic' for the debugger to determine if the current object is' real 'or' has no space to initialize ', Taking '6' bits - 'weakly_refrenced' is whether the object 'is pointed' to 'or' has ever pointed to a weak variable 'in ARC - an object without a weak reference can be freed faster - and' deallocating 'indicates whether the object is' freeing' memory - 'has_sidetable_rc' means that when the object's 'reference count' is greater than 10, you need to 'borrow this variable to store the carry' - 'extra_RC', which represents the 'object's' reference count ', actually the reference count minus 1 - if the object's 'reference count' is 10, So 'extra_rc' is 9 (this is just an example), in fact 'extra_RC' on the real iPhone is copied code that uses 19 bits to store reference countsCopy the code

For two different platforms, theisaThe storage situation of is shown in the figure

The principle of exploring

  • throughalloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZoneMethod path, found toinitInstanceIsaAnd into the realization of its principle
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { ASSERT(! cls->instancesRequireRawIsa()); ASSERT(hasCxxDtor == cls->hasCxxDtor()); // Initialize isa initIsa(CLS, true, hasCxxDtor); } copy codeCopy the code
  • Enter theinitIsaMethod, mainly to initialize the ISA pointer

The logic of the method is mainly divided into two parts – throughclsInitialize theisa– bybitsInitialize theisa

Verify the ISA pointer bit field (0-64)

Based on the aforementioned 0-64 bit fields, you can prove here that there are these bit fields in the ISA pointer by using the initIsa method (currently in macOS, so x86_64 is used).

  • First pass through mainLGPersonBreakpoint — — >initInstanceIsa –> initIsa— > goelseIn theisaInitialize the

  • Run the LLDB command:p newisa,newisaDetails of

  • So let’s keep going. Let’s go tonewisa.bits = ISA_MAGIC_VALUE;The next line, I’m going to say, isisathebitsMember assignment and re-execute the LLDB commandp newisa, the results are as follows

By comparing the information from the previous newsize, we find some changes in the ISA pointer, as shown in the figure belowmagicis59Is due toisaPointer address is translated tobinary, from47(Since there are four bit fields in front, occupying 47 bits, the address starts at 0)6Bit, and then convert toThe decimal system, as shown in the figure below

Isa’s association with classes

The shiftCLs bit field in the ISA pointer stores class information. InitInstanceIsa process is to associate the calloc pointer with the current class CLS. There are several ways to verify this:

  • 【 Method 1 】 PassinitIsaIn the methodnewisa.shiftcls = (uintptr_t)cls >> 3;validation
  • 【 Method 2 】 PassIsa pointer addresswithISA_MSAKThe value of the&To verify the
  • 【 Method 3 】 Through the runtime methodobject_getClassvalidation
  • 【 Method 4 】 PassAn operationvalidation

Method 1: Use the initIsa method

  • Run to newisa.shiftcls = (uintPtr_t) CLS >> 3; Previous step, where Shiftcls stores value information for the current class

    • At this point to seecls, it isLGPersonclass
    • shiftclsThe logic of the assignment is toLGPersonAnd after I code it,Moves to the right. 3position

  • Execute the LLDB command p (uintPtr_t) CLS, the result is (uintPtr_t) $2 = 4294975720, then shift 3 more to the right, you will get 536871965 stored in the Shiftcls of Newisa

    • p (uintptr_t)cls >> 3
    • Through the results of the previous step$2, run the LLDB commandp $2 >> 3

  • Continue executing the program toisa = newisa;Section is executed at this timep newisa

Bits assignmentThe results ofcontrast, bits ofA domainThere are two changes in —clsThe default valueAnd turned intoLGPersonThat will beIsa is perfectly related to CLSshiftclsby0Turned out to be536871965

soisaThrough theInitialize theAfter theMembers of thetheValue changing process, as shown in the figure below

Why do we need to strong-shift when assigning to shiftCLs?

Because memory storage cannot store strings, machine code can only recognize the numbers 0 and 1, so it needs to be converted to uintPtr_t data type so that the class information stored in shiftCLs can be understood by machine code, where uintPtr_t is long

Why do I need to move 3 to the right?

This is mainly because shiftcls is in the middle of the isa pointer address, and there are 3 bit fields in front of it. In order not to affect the data in the first 3 bit fields, it needs to be shifted right to wipe out the zero.

Method 2: Use isa & ISA_MSAK

  • After method 1, go back to the _class_createInstanceFromZone method. At this time, CLS and ISA have been associated. Run Po objc

  • Run x/4gx obj to get the address 0x001d8001000020e9 of the ISA pointer

  • Take the ISA pointer address & ISA_MASK (in macOS, defined using the macro in x86_64), Po 0x001d8001000020E9 & 0x00007FFFFFFFFFF8, and get LGPerson

    • arm64, the ISA_MASK macro defines a value of0x0000000ffffffff8ULL
    • x86_64, the ISA_MASK macro defines a value of0x00007ffffffffff8ULL

Method 3: Use object_getClass

By looking at the source implementation of object_getClass, you can also verify how ISA works with classes by following these steps:

  • #import
  • throughruntimeAPI, namelyobject_getClassFunction to get class information
Object_getClass (<# id_nullable obj#>) copy codeCopy the code
  • To viewobject_getClassFunction source code implementation

  • Click to enterobject_getClassThe underlying implementation

  • Enter thegetIsaSource code implementation of

  • Click on theISA(), enter the source code, you can see ifindexedType, executionifProcess, and vice versaelseprocess

– inelseIn the process, getisathebitsThis bit, again& ISA_MASKThis is consistent with the principle in Mode two,Get the current class information-From here, tooCLS and ISA are perfectly related

Method four: by bit operation

  • Go back to_class_createInstanceFromZoneMethods. throughx/4gx objgetobjThe current class information is stored inisaPointer, and isashiftclsAt this time of44Bit (because inmacOSEnvironment)

  • You want toreadIn the middle of the44positionThe class information, you need to pass throughAn operationTo the right3Bits, and the left hand side44Everything outside of bitsMaLing, itsThe relative position is constant. The bit operation process is shown in the figure, whereshiftclsIs the need toreadtheThe class information

- Shift the 'isa' address 'right 3' bits: 'p/x 0x001d8001000020e9 >> 3' to get '0x0003B0002000041D' - Shift the 'isa' address 'left 20' bits at the obtained '0x0003B0002000041D' : 'p/x 0x0003b0002000041D << 20', gets' 0x0002000041D00000 '- why is' shift 20' to the left? Since we moved 3 bits to the right, we moved 3 bits to the right, and 17 bits to the left, so we need to move 20 bits in total - to get 0x0002000041D00000 we need to move 17 bits to the right: 'p/x 0x0002000041D00000 >> 17' gets the new '0x00000001000020E8' copy codeCopy the code
  • Get the CLS address and verify with the above:p/x clsHave come to0x00000001000020e8, so it can be proved that CLS is related to ISA