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 compiled
main-arm64.cpp
To findTCJPerson
The definition of hairTCJPerson
It’s going to be compiled at the bottomstruct
The structure of the body
From the compiled main-arm64.cpp we can see:
NSObject
The underlying implementation of theisa
Pointer structure.Class
It’s just a pointer. It points toobjc_class
Type of structure.TCJPerson_IMPL
There are three member variables in the structure:isa
Inherits from the parent classNSObject
helloName
_name
- For the properties
name
: The underlying compilation generates the correspondingsetter
(_I_TCJPerson_setName_
.setter
Call within methodobjc_setProperty
Methods),getter
(_I_TCJPerson_name
), and help us convert to_name
- For member variables
helloName
: The underlying compilation does not generate the correspondingsetter
,getter
Method, 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 article
alloc
One of the core of the methodinitInstanceIsa
Method, by looking at the source implementation of this method, we found that the initializationisa
When the pointer is passedisa_t
Type initialized - And in the
NSObject
In the definitionisa
Is of typeClass
The root cause is thatisa
External feedback is the class information, in order to make developers more clear, need to be inisa
Returns with a cast similar toswift
In theas
Strong transfer. Source codeisa
The 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 correspondingsetter
andgetter
Method, and help us convert to_name
Member variables, and for member variableshelloName
: The underlying compilation does not generate the correspondingsetter
,getter
Method, and does not convert to_helloName
.One of themsetter
The implementation of a method depends onruntime
In theobjc_setProperty
.
Let’s look at the underlying implementation of objc_setProperty
-
in
objc4
Source code global searchobjc_setProperty
To findobjc_setProperty
Source code implementation -
Enter the
reallySetProperty
Source 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
- external
set
Approach: upper layer – personalization layer (e.gElegantly-named setName, setAge
Etc.) 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 oneTCJCar
Class. There are four of themBOOL
Type, respectivelyfront
,back
,left
,right
According to these four attributes, the driving direction of the car can be judged.And then let’s look at thisTCJCar
The size of memory occupied by class objects:We see oneTCJCar
The object of the class occupies 16 bytes. Including oneisa
Pointer and fourBOOL
Type attribute,8+1+1+1+1=12, according toMemory alignmentPrinciple, so oneTCJCar
The 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_frontBackLeftRight
for1
, i.e.,0b 0000 0001
, using only the last four of the eight bits separately0
or1
To represent thefront
,back
,left
,right
So at this pointfront
,back
,left
,right
Is:We can declare it separatelyfront
,back
,left
,right
To 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:
- If a byte does not have enough space left for another bit field, the bit field should be stored from the next cell.
- The length of a bitfield cannot be greater than the length of the data type itself, for example
int
The type cannot be more than 32 bits binary. - 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 tofront
Set toYES
,back
Set toNO
,left
Set toNO
,right
Set 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 timefront
Set toYES
,back
Set toNO
,left
Set toNO
,right
Set toYES
:We can see from the results that the assignment and the value can still be done. the_frontBackLeftRight
Union takes up only one byte, because in the structurefront
,back
,left
,right
Both take up only one bit of binary space, so the structure takes up only one byte, andchar
The type ofbits
It’s only one byte. They are all in a union, so they share a single byte of memory. And we areset
,get
Assignments 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 purposesOC
Let’s go back to the topic of this article and take a lookisa_t
Commonwealth source code.
④. Isa_t consortium
Through the source code we foundisa
It’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, ifcls
If it’s assigned, it’s notbits
Assign. Inisa_t
Use macros in conjunction with the bodyISA_BITFIELD
Define the bitfield, we enter the bitfield to view the source code:And we see that internally, they’re defined separatelyarm64
An architecture andx86_64
The mask and bitfield of the schema. We only analyzearm64
Is part of the content under the architecture (under the real machine environment). It can be clearly seenISA_BITFIELD
The contents of the bitfield and the maskISA_MASK
Value:0x0000000ffffffff8ULL
Let’s focus on ituintptr_t shiftcls : 33;
In theshiftcls
Which stores the memory address information of class objects and metaclass objectsisa
Pointers need to be the same asISA_MASK
It takes a bitwise and operation to get the actual address of the class object. So we’re going toISA_MASK
The value of the0x0000000ffffffff8ULL
Convert to binary numbers for analysis:You can see that in the pictureISA_MASK
When 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_MASK
Bitwise and operations can extract the memory address information of class objects and metaclass objects.
Different Architecturesisa
The occupied memory is8 bytes
–A 64 - bit
, but the internal distribution is different.arm64
architectureisa
The 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 theisa
There is no rightisa
Go into detail.
5. 2 initIsa analysis
isa_t newisa(0)
Equivalent to initializationisa
This thing,newisa.
The effect ofisa
Assign a property.SUPPORT_INDEXED_ISA
Apply toWatchOS
.isa
It is mutually exclusive as a union, andcls
,bits
isisa
Element, so when! nonpointer=true
When thecls
To perform the assignment operation, isfalse
Is thebits
Perform 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 by
main
In theTCJPerson
Breakpoint — — >initInstanceIsa
–>initIsa
–>isa_t newisa(0)
completeisa
Initialization. - perform
LLDB
Instructions:p newisa
,newisa
Details of - I’m going to go down to
newisa.bits = ISA_MAGIC_VALUE;
The next line, represented byisa
thebits
Member assignment, rerunLLDB
The commandp newisa
, the result obtained is as follows
Through with the previous onenewsize
Information comparison, foundisa
There are some changes in the pointer, as shown in the figure below
- Among them
magic
It’s 59 because it’s going toisa
The 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] Through
initIsa
In the methodnewisa.setClass(cls, this);
Method insideshiftcls = (uintptr_t)newCls >> 3
validation - [Method 2] Pass
isa
Pointer address andISA_MSAK
The value of the&
To verify the - [Method 3] Pass
runtime
The method ofobject_getClass
validation - [Mode 4] Pass
An operation
validation
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 see
cls
, it isTCJPerson
class shiftcls
The logic of assignment is to assignTCJPerson
After coding,Moves to the right
- At this point to see
-
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 ofLLDB
The commandp $2 >> 3
-
Continue executing the program to
isa = 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
cls
From the default value toTCJPerson
That will beisa
withcls
Perfect correlationshiftcls
by0
Turned out to be536875042
soisa
Through 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.m
In the import#import <objc/runtime.h>
- through
runtime
theapi
, i.e.,object_getClass
Function to get class information
object_getClass(<#id _Nullable obj#>)
Copy the code
-
To view
object_getClass
Function source code implementation -
Click to enter
object_getClass
The underlying implementation -
Enter the
getIsa
Source code implementation -
Click on the
ISA()
, enter the source code, clickgetDecodedClass
-
Then click on
getClass
-
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_createInstanceFromZone
Method. Byx/4gx obj
getobj
The current class information is stored inisa
In the pointer, andisa
In theshiftcls
At this time of44
Because of being inmacOS
Environment) -
I want to read the middle one
44
Class information, need to go throughAn operation
That will beOn the right side of the three
, andThe left hand side except for the 44 bits
allMaLing
, its relative position is constant. Its bit operation process is shown in the figure, where Shiftcls is the class information to be read- will
isa
addressMoves to the right. 3
A:p/x 0x011d800100008111 >> 3
,0x0023b00020001022
- I’m going to get
0x0023b00020001022
The left 20
:p/x 0x0023b00020001022 << 20
,0x0002000102200000
- Why is it
The left 20
? becauseWe moved three places to the right
, which is equivalent toIt's shifted three places to the right
And theOn the left
Need to beMaLing
The figures are17
So we need to move all together20
- Will get the
0x0002000041d00000
再Moves to the right of 17
:p/x 0x0002000102200000 >> 17
Get new0x0000000100008110
- will
-
To obtain
cls
Verify with the above address:p/x cls
Have come to0x0000000100008110
So it can be provedcls
与isa
It’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
class
In fact, andInstance objects
Again, it’s instantiated by the parent — the parent of the class is calledThe metaclass
.Let’s go firstp/x
Print the memory address of the class and use it againx/4gx
Print the memory structure to the correspondingisa
And use it to& ISA_MASK
Offset to getisa
Pointing to the superior (equivalent toobject_getClass
) in turn.1) printTCJPerson class
achieveisa
② 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 isNSObject
Its 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
在main
beforeTCJPerson class
andTCJPerson metaclass
It 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_class
Pointer, which represents the relation of inheritance chain. Dotted line isisa
Pointer.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)
instance
The object’sisa
Point to theclass
objectclass
The object’sisa
Point to themeta-class
objectmeta-class
The object’sisa
Points to the base classmeta-class
object
Write in the back
Study harmoniously without being impatient. I’m still me, a different color of fireworks.