IOS martial arts esoteric article summary

Writing in the front

In the iOS of the secret book ② : OC object principle – (memory alignment and MALLOc source analysis) in the article talked about the object attributes in memory alignment and MALloc source analysis, then we will analyze the isa initialization and pointing analysis and the nature of the object

A possible secret Demo for this section

First, the nature of the object

① Understanding of Clang

  • Clang is a lightweight C/C++/Objective-C compiler written by Apple and based on LLVM. The source code is published under the LLVM BSD protocol. Clang will support its normal lambda expressions, simplified handling of return types, and better handling of constEXPr keywords.

  • It is almost completely compatible with the GNU C language specification (although there are some incompatibables, including some differences in compiler command options) and adds additional syntactical features such as C function overloading (which modifies functions by __attribute__((overloadable)), One of its goals is to go beyond GCC.

  • It is mainly used for low-level compilation, output some OC files into C++ files, such as main.m output into main. CPP, its purpose is to better observe some low-level structure and implementation logic, easy to understand the underlying principle

② Clang operation instruction

// compile the object file into a c++ file -- compile main.m into main.cpp
clang -rewrite-objc main.m -o main.cpp 

// UIKit is reporting an error -- compiling viewController.m to viewController.cpp
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios13.0. 0 -isysroot /
Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14. 0.sdk ViewController.m 

// the 'xcrun' command has been installed with the 'xcrun' command. The 'xcrun' command has been encapsulated with the 'clang' command to make it easier to useXcrun - SDK iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (emulator) xcrun - SDK iphoneOS clang -arch arm64 -rewrite-objc main.m -o mainarm64. CPP (mobile)Copy the code

③ Explore the nature of objects

  • Build test code

  • From the terminal, use clang to compile main.m into main. CPP, and enter the following command on the terminal

    • xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
  • Open up compiledmain-arm64.cppTo findTCJPersonThe definition of hairTCJPersonIt’s going to be compiled at the bottomstructThe structure of the body

From the compiled main-arm64.cpp we can see:

  • NSObjectThe underlying implementation of theisaPointer structure.
  • ClassIt’s just a pointer. It points toobjc_classType of structure.
  • TCJPerson_IMPLThere are three member variables in the structure:
    • isaInherits from the parent classNSObject
    • helloName
    • _name
  • For the propertiesname: The underlying compilation generates the correspondingsetter(_I_TCJPerson_setName_.setterCall within methodobjc_setPropertyMethods),getter(_I_TCJPerson_name), and help us convert to_name
  • For member variableshelloName: The underlying compilation does not generate the correspondingsetter,getterMethod, and does not convert to_helloName

From the above analysis, you understand the nature of the OC object, the structure, but when you look at the definition of NSObject, it raises a question: why is isa of type Class?

  • inOC object principle – on (alloc & init & new)It was mentioned in the articleallocOne of the core of the methodinitInstanceIsaMethod, by looking at the source implementation of this method, we found that the initializationisaWhen the pointer is passedisa_tType initialized
  • And in theNSObjectIn the definitionisaIs of typeClassThe root cause is thatisaExternal feedback is the class information, in order to make developers more clear, need to be inisaReturns with a cast similar toswiftIn theasStrong transfer. Source codeisaThe strong transformation of is shown in the figure below

④ Explore the attribute get and set methods

Through the above analysis, we know: for attributesname: The underlying compilation generates the correspondingsetterandgetterMethod, and help us convert to_nameMember variables, and for member variableshelloName: The underlying compilation does not generate the correspondingsetter,getterMethod, and does not convert to_helloName.One of themsetterThe implementation of a method depends onruntimeIn theobjc_setProperty.

Let’s look at the underlying implementation of objc_setProperty

  • inobjc4Source code global searchobjc_setPropertyTo findobjc_setPropertySource code implementation

  • Enter thereallySetPropertySource code implementation, the principle of its method is the new valueretain, the old valuerelease

Summary: After exploring the underlying source code of objc_setProperty, the following points are noted:

  • The purpose of the objc_setProperty method is to associate the upper set method with the lower set method, which is essentially an interface

  • The reason for this design is that there are a lot of upper set methods, and if you call the lower set method directly, there will be a lot of temporary variables, and when you want to find an SEL, it will be very troublesome

  • Based on the above reasons, apple has adopted the adapter design pattern (the underlying interface adapter for the client required interfaces), provides an interface that is for the use of the upper set method, internal calls at the bottom of the set method, make its are not affected each other, so no matter how the upper and the lower is constant, change can affect the upper or lower level, It is mainly used to isolate the upper and lower interfaces.

The following figure shows the relationship between the top layer, the isolation layer, and the bottom layer

  • externalsetApproach: upper layer – personalization layer (e.gElegantly-named setName, setAgeEtc.)
  • objc_setProperty: interface isolation layer (translating external information into operations on memory addresses and values)
  • reallySetProperty: Bottom implementation layer (assignment and memory management)

Ii. Underlying principles of ISA

Alloc & Init & New on iOS Martial Arts Secrets ① : OC Object Principle (Alloc & Init & New) OC object principles – The first two of alloc’s 3 cores are analyzed in (memory alignment and Malloc source code analysis). Today, explore how initInstanceIsa associates CLS with ISA.

Before we do that, we need to understand what a union is and why the type ISA_t of ISA is defined using a union. So what is a union? What is a bitfield?

(1). A domain

(1). 1 definition

Some information is stored in one or more binary bits rather than a complete byte. For example, when storing a switch quantity, there are only 0 and 1 states, and 1 bit binary can be used. To save storage space and simplify processing, C provides a data structure called a bit field or bit segment.

The so-called bit-field is to divide the binary bits in a byte into several different regions and specify the number of bits in each region. Each domain has a domain name, which allows you to operate by domain name in your program — so that several different objects can be represented in a one-byte binary field.

①.2 Comparison with structures

Bit-fields are used similar to structures and are themselves a type of structure.

/ / structure
struct TCJStruct {
    // (type specifier element);
    char a;
    int b;
} TCJStr;

/ / a domain
struct TCJBitArea {
    // (type specifier bit domain name: bit field length);
    char a: 1;
    int b: 3;
} TCJBit;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(% % @ "Struct: lu - BitArea: lu".sizeof(TCJStr), sizeof(TCJBit));
    }
    return 0;
}

Copy the code

Output Struct: 8 — BitArea: 4.

Complexes (2).

(2). 1 definition

When multiple data needs to share memory or multiple data needs to be fetched only for a moment at a time, you can use a union.

  • A union is a structure
  • All its members have an offset of 0 relative to the base address
  • The structure should be large enough to accommodate the widest members
  • Variables are “mutually exclusive” — they share a common memory header. Joint variables can be assigned any member value, but only one value can be assigned at a time. New values flush out old values.

②.2 Comparison with structure

Each member of the structure is stored sequentially, and the offset address of all members of the union is 0, that is, all members are stacked on top of each other, so only one member of the union is valid at any one time — the size of the structure depends on all the elements, and the union depends on the largest one

②.3 Supplementary knowledge — bit operators

In computer language, there are many arithmetic operators besides addition, subtraction, multiplication, division and so on. Here is a brief explanation of bitwise operators. Bitwise operators operate on bits. Of course, operands can only be integer and character data. Six kinds of an operator in the C language: & bitwise and, | bitwise or, ^ bitwise exclusive or, ~, < < left and > > moves to the right. We still refer to the light switch theory above, except now we have two switches: switch A and switch B, with 1 for on and 0 for off.

1) by bit and &

So 0 goes out 0, all 1 goes out 1.

A B &
0 0 0
1 0 0
0 1 0
1 1 1

We can understand that in bitwise and operation, the two switches are in series. If we want to light up, we need to turn on both switches to light up, so 1&1 = 1. If either switch is not turned on, the light will not come on, so all other operations will be 0.

2) bitwise or |

I have 1 out 1, all 0 out 0.

A B I
0 0 0
1 0 1
0 1 1
1 1 1

In bitwise or operation, we can understand that the two switches are in parallel, that is, when a switch is on, the lamp will be on. Only if both switches are off. The lights don’t work.

3) xor ^ by bit

Equal to 0, different to 1.

A B ^
0 0 0
1 0 1
0 1 1
1 1 0
4) not ~

Either operation or inverse operation, in binary 1 becomes 0, 0 becomes 1. For example, 110101 is 001010 after non-calculation, that is, 1010.

5) left < <

The left shift operation is to move all the binaries of the operands on the left of << several bits to the left. The number of bits moved is the value of the number on the right of <<. The high part is discarded and the low part is filled with 0. To the left n is 2 to the n. For example: A <<4 means to move each binary of a four to the left. For example, if a=00000011(decimal 3), the value is 00110000(decimal 48).

6) moves to the right > >

The right shift operation is to move all the binaries of the >> left operand several right bits, and the >> right number specifies the number of digits to move. For example, if a=15, a>>2 indicates that 00001111 is right-shifted to 00000011(decimal 3).

②. The use of 4-bit operators

1) values

You can use bitwise and & operation to take out the value of the specified bit. The specific operation is to take out the value of which bit will be 1, the other bits are 0, and then carry out bitwise and calculation with the original data, you can take out the specific bit.

Example: 0000 0011 Take the third from last value

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000Copy the code

In the example above, we value from 00000011, and 00000011 is called source. The 0000 0100 that is set by bit and operation is called the mask.

2) set value

Bitwise or | operator to a particular value is set to 1 or bitwise or & operator to a certain value to 0. The specific operation is: if you want to set the value of a certain bit to 1, then the value of the corresponding bit in the mask is set to 1, and the other bits of the mask are 0, and the source code and the mask can be operated by bit or.

Example: Change the third from last of 0000 0011 to 1

/ / change the value of the third from bottom to mask the bottom third of value set to 1, the other one is 0, with source code bitwise or operation 0011 | 0000 0100-0000 -- -- -- -- -- -- -- -- -- -- - 0000/0111 / third from bottom in the source code can be to change the value of 1Copy the code

If you want to set the value of a certain bit to 0, then set the value of the corresponding bit in the mask to 0, and the other bits of the mask to 1, and operate the source code and the mask by bit.

Example: Change the second-to-last value of 0000 0011 to 0

/ / change the value of the second from bottom to mask the bottom the second set to 0, the value of the other bits to 1, with source code bitwise or operation 0011 | 1111 1101-0000 -- -- -- -- -- -- -- -- -- -- - 0000/0001 / second from bottom in the source code can be to change the value of 0Copy the code

I believe you have some understanding of bitwise operators.

(3) The use of structural position domain and union

Let’s look at the following 🌰: we declare oneTCJCarClass. There are four of themBOOLType, respectivelyfront,back,left,rightAccording to these four attributes, the driving direction of the car can be judged.And then let’s look at thisTCJCarThe size of memory occupied by class objects:We see oneTCJCarThe object of the class occupies 16 bytes. Including oneisaPointer and fourBOOLType attribute,8+1+1+1+1=12, according toMemory alignmentPrinciple, so oneTCJCarThe object of class is 16 bytes.

We know that there are only two cases where a BOOL takes up one byte of memory :0 or 1. There are eight bits in a byte of memory, and the bits are only zeros or ones, so we can use one bit to represent a BOOL. This means that the four BOOL values we declared above only end up using four bits, which saves memory. So how do we do that? To implement four BOOL values in one byte, we can use a member variable of type CHAR, which occupies one byte of memory space, that is, eight binary bits. You can use the last four bits to store four BOOL values. Of course, we can’t write char as an attribute, because once we write it as an attribute, the system automatically adds member variables and implements set and GET methods for us.

@interface TCJCar(){
    char _frontBackLeftRight;
}
Copy the code

If we assign_frontBackLeftRightfor1, i.e.,0b 0000 0001, using only the last four of the eight bits separately0or1To represent thefront,back,left,rightSo at this pointfront,back,left,rightIs:We can declare it separatelyfront,back,left,rightTo facilitate the next bit operation value and assignment:

#define TCJDirectionFrontMask 0b00001000 // This binary number corresponds to the decimal number 8 #define TCJDirectionBackMask 0B00000100 // this binary number corresponds to the decimal number 4 #define TCJDirectionLeftMask 0b00000010 // This binary number corresponds to the decimal number 2 #define TCJDirectionRightMask 0b00000001 // this binary number corresponds to the decimal number 1Copy the code

With an understanding of the bitwise operators’ left shift << and right shift >>, we can optimize the above code to:

#define TCJDirectionFrontMask    (1 << 3)
#define TCJDirectionBackMask     (1 << 2)
#define TCJDirectionLeftMask     (1 << 1)
#define TCJDirectionRightMask    (1 << 0)
Copy the code

The custom set method is as follows:

- (void) setFront: (BOOL) front {the if (front) {/ / if you need to value set to 1, the source and mask for bitwise or _frontBackLeftRight | = TCJDirectionFrontMask; } else {// If need to set the value to 0 // source and bitwise inverse mask bitwise operation _frontBackLeftRight &= ~TCJDirectionFrontMask; } } - (void)setBack:(BOOL)back { if (back) { _frontBackLeftRight |= TCJDirectionBackMask; } else { _frontBackLeftRight &= ~TCJDirectionBackMask; } } - (void)setLeft:(BOOL)left { if (left) { _frontBackLeftRight |= TCJDirectionLeftMask; } else { _frontBackLeftRight &= ~TCJDirectionLeftMask; } } - (void)setRight:(BOOL)right { if (right) { _frontBackLeftRight |= TCJDirectionRightMask; } else { _frontBackLeftRight &= ~TCJDirectionRightMask; }}Copy the code

The custom GET methods are as follows:

- (BOOL)isFront { return !! (_frontBackLeftRight & TCJDirectionFrontMask); } - (BOOL)isBack { return !! (_frontBackLeftRight & TCJDirectionBackMask); } - (BOOL)isLeft { return !! (_frontBackLeftRight & TCJDirectionLeftMask); } - (BOOL)isRight { return !! (_frontBackLeftRight & TCJDirectionRightMask); }Copy the code

Note here that in the code! Is a logical operator, because _frontBackLeftRight & TCJDirectionFrontMask must return an integer. For example, if front is YES, the binary number is 0B 0000 1000, and the corresponding decimal number is 8. So after a logical non-operation,! The value of (8) is 0. Perform another logical nonoperation on 0! (0), so that’s 1, so that corresponds to front being YES. So there are two logical non-operations here!! Of course, we also need to implement the initialization method:

- (instancetype)init
{
    self = [super init];
    if (self) {
        _frontBackLeftRight = 0b00001000;
    }
    return self;
}
Copy the code

By testing, we have completed the evaluation and assignment:

③.1 Optimize code using structural position fields

We talked about bit-fields earlier, so we can use structural bit-fields to optimize our code. This eliminates the need to declare the mask part of the code above. The bit-field declaration format is bit-domain: bit-field length. Note the following when using bitfields:

  1. If a byte does not have enough space left for another bit field, the bit field should be stored from the next cell.
  2. The length of a bitfield cannot be greater than the length of the data type itself, for exampleintThe type cannot be more than 32 bits binary.
  3. A bit-field can have no bit-domain name, which is used only for padding or repositioning. Nameless bit fields are not available.

Using bit-field optimized code:

Let’s test that out and see if that’s true. This time we’re going tofrontSet toYES,backSet toNO,leftSet toNO,rightSet toYES:Assignments and values can still be done. However, after the code was optimized in this way, we removed the mask and initialization code, and the readability was very poor. We continued to use the union for optimization:

③.2 Use consortia to optimize code

We can use more efficient bit operation to assign and value, and use union union to store data. This not only increases reading efficiency, but also improves code readability.

#import "tcjcar. h" //#define TCJDirectionFrontMask 0b00001000 //#define TCJDirectionBackMask 0B00000100 //#define TCJDirectionLeftMask 0b00000010 //#define TCJDirectionRightMask 0b00000001 #define TCJDirectionFrontMask (1 << 3) #define TCJDirectionBackMask (1 << 2) #define TCJDirectionLeftMask (1 << 1) #define TCJDirectionRightMask (1 << 0) @interface TCJCar() { union{ char bits; Struct {char front: 1; struct {char front: 1; char back : 1; char left : 1; char right : 1; }; }_frontBackLeftRight; } @end @implementation TCJCar - (instancetype)init { self = [super init]; if (self) { _frontBackLeftRight.bits = 0b00001000; } return self; } - (void)setFront:(BOOL)front { if (front) { _frontBackLeftRight.bits |= TCJDirectionFrontMask; } else { _frontBackLeftRight.bits &= ~TCJDirectionFrontMask; } } - (BOOL)isFront { return !! (_frontBackLeftRight.bits & TCJDirectionFrontMask); } - (void)setBack:(BOOL)back { if (back) { _frontBackLeftRight.bits |= TCJDirectionBackMask; } else { _frontBackLeftRight.bits &= ~TCJDirectionBackMask; } } - (BOOL)isBack { return !! (_frontBackLeftRight.bits & TCJDirectionBackMask); } - (void)setLeft:(BOOL)left { if (left) { _frontBackLeftRight.bits |= TCJDirectionLeftMask; } else { _frontBackLeftRight.bits &= ~TCJDirectionLeftMask; } } - (BOOL)isLeft { return !! (_frontBackLeftRight.bits & TCJDirectionLeftMask); } - (void)setRight:(BOOL)right { if (right) { _frontBackLeftRight.bits |= TCJDirectionRightMask; } else { _frontBackLeftRight.bits &= ~TCJDirectionRightMask; } } - (BOOL)isRight { return !! (_frontBackLeftRight.bits & TCJDirectionRightMask); } @endCopy the code

Let’s test that out and see if that’s true, and we’ll do it again this timefrontSet toYES,backSet toNO,leftSet toNO,rightSet toYES:We can see from the results that the assignment and the value can still be done. the_frontBackLeftRightUnion takes up only one byte, because in the structurefront,back,left,rightBoth take up only one bit of binary space, so the structure takes up only one byte, andcharThe type ofbitsIt’s only one byte. They are all in a union, so they share a single byte of memory. And we areset,getAssignments and values in the method are enhanced by bitwise operations using masks, and the overall logic is clear. But if we were to write code like this in daily development, we would probably get killed by our colleagues. While the code is clear, the overall reading is still difficult. We learned about bit operations and unions here, but more for our reading purposesOCLet’s go back to the topic of this article and take a lookisa_tCommonwealth source code.

④. Isa_t consortium

Through the source code we foundisaIt’s a union, and a union is a structure that’s eight bytes long, and its property is shared memory, or shared memoryThe mutexFor example, ifclsIf it’s assigned, it’s notbitsAssign. Inisa_tUse macros in conjunction with the bodyISA_BITFIELDDefine the bitfield, we enter the bitfield to view the source code:And we see that internally, they’re defined separatelyarm64An architecture andx86_64The mask and bitfield of the schema. We only analyzearm64Is part of the content under the architecture (under the real machine environment). It can be clearly seenISA_BITFIELDThe contents of the bitfield and the maskISA_MASKValue:0x0000000ffffffff8ULLLet’s focus on ituintptr_t shiftcls : 33;In theshiftclsWhich stores the memory address information of class objects and metaclass objectsisaPointers need to be the same asISA_MASKIt takes a bitwise and operation to get the actual address of the class object. So we’re going toISA_MASKThe value of the0x0000000ffffffff8ULLConvert to binary numbers for analysis:You can see that in the pictureISA_MASKWhen the value of is converted into binary, 33 bits are 1, and the bitwise and operation mentioned above can extract the value of these 33 bits. So that means the sameISA_MASKBitwise and operations can extract the memory address information of class objects and metaclass objects.

Different ArchitecturesisaThe occupied memory is8 bytesA 64 - bit, but the internal distribution is different.arm64architectureisaThe distribution of internal members is shown as follows

  • Nonpointer: indicates whether pointer optimization is enabled for isa Pointers. 0: indicates pure ISA Pointers. 1: Not only class object address, ISA contains class information, object reference count, etc

  • Has_assoc: flag bit of the associated object. 0 does not exist and 1 exists

  • 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: Stores the value of a class pointer (the address of a class), that is, the memory address information of a class or metaclass object. With pointer optimization turned on, 33 bits are used to store class Pointers in the ARM64 architecture

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

  • Weakly_referenced: Weak variable of whether an object is pointed to or used to point to an ARC. Objects without weak references can be released faster

  • Deallocating: Indicates whether the object is freeing memory

  • Has_sidetable_rc: When the object reference technique is greater than 10, this variable is borrowed to store carry

  • Extra_rc: When representing the reference count of this object, the reference count is actually subtracted by 1. For example, if the object’s reference count is 10, the extra_rc is 9. If the reference count is greater than 10, the following has_sideTABLE_rc is used

When the object reference technique is greater than 10, that is an example, not a specific 10.

Now we have a new understanding of isa Pointers. After the ARM64 architecture, ISA Pointers do not only store the memory addresses of class and metaclass objects, but also store more information in a union. Shiftcls stores the memory addresses of class and metaclass objects. The memory address value can be extracted by bitwise ampersand operation with ISA_MASK.

(5) Exploring the isa principle

⑤.1 Isa initialization

In the previousOC object principle – on (alloc & init & new)It was an understatement in the articleobj->initInstanceIsa(cls, hasCxxDtor)Only internal calls are knowninitIsa(cls, true, hasCxxDtor)Initialize theisaThere is no rightisaGo into detail.

5. 2 initIsa analysis

  • isa_t newisa(0)Equivalent to initializationisaThis thing,newisa.The effect ofisaAssign a property.
  • SUPPORT_INDEXED_ISAApply toWatchOS.isaIt is mutually exclusive as a union, andcls,bitsisisaElement, so when! nonpointer=trueWhen theclsTo perform the assignment operation, isfalseIs thebitsPerform assignment operations (all family members share the same memory address anyway).

⑤.3 Verifying isa pointer bit fields (0-64)

Based on the 0-64 bit fields mentioned earlier, you can prove these bits in the ISA pointer here using the initIsa method (currently on macOS, so x86_64 is used).

  • First of all bymainIn theTCJPersonBreakpoint — — >initInstanceIsa –> initIsa –> isa_t newisa(0)completeisaInitialization.
  • performLLDBInstructions:p newisa,newisaDetails of
  • I’m going to go down tonewisa.bits = ISA_MAGIC_VALUE;The next line, represented byisathebitsMember assignment, rerunLLDBThe commandp newisa, the result obtained is as follows

Through with the previous onenewsizeInformation comparison, foundisaThere are some changes in the pointer, as shown in the figure below

  • Among themmagicIt’s 59 because it’s going toisaThe pointer address is converted to binary, reading 6 bits from 47 (because there are 4 fields in front of it, 47 bits are used, and the address starts from 0), and then converted to decimal, as shown in the figure below

The association between ISA and classes

The principle of CLS association with ISA is that the shiftCls bit field in isa pointer stores the class information, wherein initInstanceIsa associates the pointer returned by Calloc with the current CLS class. There are several verification methods as follows:

  • [Method 1] ThroughinitIsaIn the methodnewisa.setClass(cls, this);Method insideshiftcls = (uintptr_t)newCls >> 3validation
  • [Method 2] PassisaPointer address andISA_MSAKThe value of the&To verify the
  • [Method 3] PassruntimeThe method ofobject_getClassvalidation
  • [Mode 4] PassAn operationvalidation

Method 1: Use the initIsa method

  • Run newisa.setclass (CLS, this); Shiftcls = (uintptr_t)newCls >> 3; Previous step, where Shiftcls stores the value information for the current class

    • At this point to seecls, it isTCJPersonclass
    • shiftclsThe logic of assignment is to assignTCJPersonAfter coding,Moves to the right
  • Execute the LLDB command p (Uintptr_t) CLS, the result is (uintptr_t) $2 = 4295000336, move it three places to the right, choose one of the following ways to get 536875042 and store it in shiftcls of Newisa

    • p (uintptr_t)cls >> 3
    • Through the results of the previous step$2, the implementation ofLLDBThe commandp $2 >> 3
  • Continue executing the program toisa = newisa;Part is executed at this timep newisa

There are two changes in the bit field of bits compared to the result of the bits assignment

  • clsFrom the default value toTCJPersonThat will beisawithclsPerfect correlation
  • shiftclsby0Turned out to be536875042

soisaThrough the value change process of the initialized member, as shown in the figure below

Why do I need a strong type when SHIFtcls is assigned? Since the memory store cannot store strings, machine code can only recognize 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 type.

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 three bit fields in front of it. In order not to affect the data in the first three bit fields, it needs to be zeroed by right shift.

Method 2: Use ISA & ISA_MSAK

Method 3: Use object_getClass

You can also verify isa’s association with a class by viewing object_getClass’s source code implementation. There are several steps:

  • main.mIn the import#import <objc/runtime.h>
  • throughruntimetheapi, i.e.,object_getClassFunction to get class information
object_getClass(<#id _Nullable obj#>)
Copy the code
  • To viewobject_getClassFunction source code implementation

  • Click to enterobject_getClassThe underlying implementation

  • Enter thegetIsaSource code implementation

  • Click on theISA(), enter the source code, clickgetDecodedClass

  • Then click ongetClass

  • This is consistent with the principle in approach 2, which is to obtain the current class information, from which it can also be concluded that CLS and ISA are perfectly correlated

Mode 4: Through bit operation

  • Go back to_class_createInstanceFromZoneMethod. Byx/4gx objgetobjThe current class information is stored inisaIn the pointer, andisaIn theshiftclsAt this time of44Because of being inmacOSEnvironment)

  • I want to read the middle one44Class information, need to go throughAn operationThat will beOn the right side of the three, andThe left hand side except for the 44 bitsallMaLing, its relative position is constant. Its bit operation process is shown in the figure, where Shiftcls is the class information to be read

    • willisaaddressMoves to the right. 3A:p/x 0x011d800100008111 >> 3,0x0023b00020001022
    • I’m going to get0x0023b00020001022 The left 20:p/x 0x0023b00020001022 << 20,0x0002000102200000
    • Why is itThe left 20? becauseWe moved three places to the right, which is equivalent toIt's shifted three places to the rightAnd theOn the leftNeed to beMaLingThe figures are17So we need to move all together20
    • Will get the0x0002000041d00000Moves to the right of 17:p/x 0x0002000102200000 >> 17Get new0x0000000100008110
  • To obtainclsVerify with the above address:p/x clsHave come to0x0000000100008110So it can be provedclsisaIt’s correlated.

3. Isa position analysis

③. Class 1 will only have one copy in memory

We all know that objects can be created more than one, but can classes be created more than one? The answer is one. How do you test it? Let’s look at the following code and print the result:This is proved by running resultsThere will only be one copy of the class in memory.

③ 2.1 Viewing THE ISA Direction by Object or Class

classIn fact, andInstance objectsAgain, it’s instantiated by the parent — the parent of the class is calledThe metaclass.Let’s go firstp/xPrint the memory address of the class and use it againx/4gxPrint the memory structure to the correspondingisaAnd use it to& ISA_MASKOffset to getisaPointing to the superior (equivalent toobject_getClass) in turn.1) printTCJPerson classachieveisa

② The TCJPerson metaclass pointer is offset by the TCJPerson class, and the TCJPerson metaclass pointer is printed to obtain ISA

③ Offset the TCJPerson metaclass to get the NSObject root metaclass pointer. Print the NSObject root metaclass to get ISA

④ The NSObject root metaclass is offset to get the NSObject root metaclass itself pointer

⑤ Print the NSObject root class to get ISA

⑥ The NSObject root metaclass pointer is offset by the NSObject root class

Conclusion: ① Instance object -> Class object -> metaclass -> root metaclass -> root metaclass (itself)

②NSObject (root) -> root metaclass -> root metaclass

Isas that point to the root metaclass are the same

③ 2.2 Viewing the ISA Direction using NSObject

Because it isNSObjectIts metaclass is the root classA metaclass— Output availableThe root metaclass points to itself

③ 2.3 Prove that classes and metaclasses are created by the system

(1) Perjury during operation

mainbeforeTCJPerson classandTCJPerson metaclassIt is already in memory, but the program is already running, which is not convincing.

② Check MachO file method

After compiling the project, useMachoViewOpen program binary executable file to view:

Conclusion:

  • Objects are instantiated from classes by programmers (apes)
  • Classes are written in code, with only one copy in memory, and are created by the system
  • Metaclasses are created by the system compiler at system compilation time to facilitate method compilation

3 ISA goes bitmap

Let’s summarize the figure above: the solid line in the figure issuper_classPointer, which represents the relation of inheritance chain. Dotted line isisaPointer.Isa route (dotted line) :Instance object -> Class object -> metaclass -> root metaclass -> root metaclass (itself) Inheritance relationship (solid line) :NSObject has a nil parent, and the parent of the root metaclass is NSObject

1.Root class(class) is really NSObject, and NSObject has no superclass, so the superclass of Root class(class) points to nil(NSObject superclass is nil).

2. Each Class has an ISA pointer to a unique Meta Class.

3. The superclass of Root class(meta) points to Root class(class), which is NSObject, forming a loop. This shows that Root class(meta) inherits from Root class(class)(the parent of the Root metaclass is NSObject).

4. Isa pointer to each Meta class points to Root class (Meta)

  • instanceThe object’sisaPoint to theclassobject
  • classThe object’sisaPoint to themeta-classobject
  • meta-classThe object’sisaPoints to the base classmeta-classobject

Write in the back

Study harmoniously without being impatient. I’m still me, a different color of fireworks.