(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
clang
Is aApple
Lead writing, based onLLVM
theC/C++/OC compiler
- Mainly used for
The underlying compiler
To someFile ' 'output
intoC + + files
, e.g.main.m
The output intomain.cpp
For better observationThe underlying
Some of thestructure
及implementation
Logic, easy to understand the underlying principles.
Explore the nature of objects
- in
main
To 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, using
clang
willmain.m
Compiled intomain.cpp
There 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_IMPL
The first property in theisa
Is inherited fromNSObject
, belong toPseudo inheritance
, pseudo-inheritedway
Is directly to theNSObject
The structure is defined asLGPerson
In theThe first attribute
That means thatLGPerson
haveNSObject
In theAll member variables
.LGPerson
The first property inNSObject_IVARS
Equivalent toNSObject
In 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: whyisa
Is of typeClass
?
- inIOS – Underlying principles 02: Alloc & Init & New source code analysisIt’s mentioned in the article
alloc
One of the core methodsinitInstanceIsa
Method, by looking at the source implementation of this method, we find that the initializationisa
Pointer is passedisa_t
Type initialized, - And in the
NSObject
The type of ISA in the definition isClass
The fundamental reason is thatisa
The external feedback isThe class information
In order to make developers moreclear
That need to be inisa
I did one on the returnType cast
, similar to theswift
In theas
The strong. In the sourceisa
theStrong go
See the figure below
conclusion
Therefore, it can be concluded from the above exploration process:
The nature of the OC object
In fact, isThe structure of the body
LGPerson
In theisa
Is inherited fromNSObject
In theisa
Objc_setProperty source code exploration
In addition toLGPersong
We find that there are also propertiesname
The correspondingset
和 get
Method, as shown in the figure below, whereset
Method implementation depends on runtimeobjc_setProperty
.
You can take the following steps to unravel the underlying implementation of objc_setProperty
- Search globally in objC4-781
objc_setProperty
To findobjc_setProperty
Source code implementation of
- Enter the
reallySetProperty
Source 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_setProperty
The purpose of the method is to applyCorrelation of the upper
theset
Methods andThe underlying
theset
Method, its essence is ainterface
- It’s designed this way
why
Is that,The upper
theset
There’s a lot of ways to do that ifCall the underlying set directly
Method, there will be a lot ofTemporary variable
When you want toTo find the
For an SEL, it’s going to be verytrouble
- For these reasons, Apple adopted
Adapter design pattern (adapting the underlying interface to the interface required by the client)
.foreign
To provide ainterface
For the upper set method,internal
Call the underlyingSet method
, so that they are not affected by each other, i.eNo matter how the top changes, the bottom stays the same, right
Or,Changes at the bottom do not affect those at the top
To 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, compare
Waste of memory
Suppose there are four int members allocated16
Bytes of memory, but in use, you only use4
Bytes, the rest12
Bytes are a waste of memory - Advantages: Storage
Capacity 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 the
Each member consumes different amounts of memory
With each otherHave no effect on
- Share body
All members occupy the same memory segment
, modifying a member willimpact
All the other members
- Structure of the
-
Memory allocation size
- Structure in vivo
> =
Occupied by all membersThe sum of memory
(There may be gaps between members) The appropriate
The amount ofmemory
Is equal to theLargest member
The amount ofmemory
- Structure in vivo
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
- through
cls
Initialization,Bits There is no default value
- through
bits
Initialization,CLS has a default value
- through
-
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
– nonpointer
There are two values, representing the custom class, etc1
A –0
:Pure isa pointer
– 1
: not onlyClass object address
, is included in the ISAThe class information
And the object’sReference counting
Etc.
- ` 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, theisa
The storage situation of is shown in the figure
The principle of exploring
- through
alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone
Method path, found toinitInstanceIsa
And 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 the
initIsa
Method, mainly to initialize the ISA pointer
The logic of the method is mainly divided into two parts – throughcls
Initialize theisa
– bybits
Initialize 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 main
LGPerson
Breakpoint — — >initInstanceIsa
–>initIsa
— > goelse
In theisa
Initialize the
- Run the LLDB command:
p newisa
,newisa
Details of
- So let’s keep going. Let’s go to
newisa.bits = ISA_MAGIC_VALUE;
The next line, I’m going to say, isisa
thebits
Member 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 below–magic
is59
Is due toisa
Pointer address is translated tobinary
, from47
(Since there are four bit fields in front, occupying 47 bits, the address starts at 0)6
Bit, 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 】 Pass
initIsa
In the methodnewisa.shiftcls = (uintptr_t)cls >> 3;
validation - 【 Method 2 】 Pass
Isa pointer address
withISA_MSAK
The value of the&
To verify the - 【 Method 3 】 Through the runtime method
object_getClass
validation - 【 Method 4 】 Pass
An operation
validation
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 see
cls
, it isLGPerson
class shiftcls
The logic of the assignment is toLGPerson
And after I code it,Moves to the right. 3
position
- At this point to see
-
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 to
isa = newisa;
Section is executed at this timep newisa
与Bits assignment
The results ofcontrast
, bits ofA domain
There are two changes in —cls
由The default value
And turned intoLGPerson
That will beIsa is perfectly related to CLS
– shiftcls
by0
Turned out to be536871965
soisa
Through theInitialize the
After theMembers of the
theValue 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
- through
runtime
API, namelyobject_getClass
Function to get class information
Object_getClass (<# id_nullable obj#>) copy codeCopy the code
- To view
object_getClass
Function source code implementation
- Click to enter
object_getClass
The underlying implementation
- Enter the
getIsa
Source code implementation of
- Click on the
ISA()
, enter the source code, you can see ifindexed
Type, executionif
Process, and vice versaelse
process
– inelse
In the process, getisa
thebits
This bit, again& ISA_MASK
This 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_createInstanceFromZone
Methods. throughx/4gx obj
getobj
The current class information is stored inisa
Pointer, and isashiftcls
At this time of44
Bit (because inmacOS
Environment)
- You want to
read
In the middle of the44
positionThe class information
, you need to pass throughAn operation
To the right3
Bits, and the left hand side44
Everything outside of bitsMaLing
, itsThe relative position is constant
. The bit operation process is shown in the figure, whereshiftcls
Is the need toread
theThe 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 cls
Have come to0x00000001000020e8
, so it can be proved that CLS is related to ISA