Summary of basic principles of iOS
In the alloc & Init & New article, alloc has three core operations, one of which is obj->initInstanceIsa: the class is associated with ISA, and that’s what we’ll explore today.
The main purpose of this article is to understand how classes relate to ISA. Before we get into the body, we need to understand one concept: What is the nature of OC objects?
OC Object nature
Before we explore the nature of OC objects, let’s look at one compiler: Clang
Clang
clang
Is aApple
Lead writing, based onLLVM
theC/C++/OC compiler
- Chiefly used of
The underlying compiler
To someThe output file
intoC + + files
, e.g.main.m
The output intomain.cpp
The purpose is for better observationThe underlying
Some of thestructure
及implementation
It is convenient to understand the underlying principle.
Exploring the nature of objects
- in
main
To customize a classLGPerson
, has an attribute name
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation LGPerson
@end
Copy the code
- Through the terminal, using
clang
willmain.m
Compiled intomain.cpp
There are several build commands, the first of which is used here
CPP clang -rewrite-objc main.m -o main. CPP //2, viewController.m to 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, Use the xcode tool xcrun //3, emulator file compilation -xcrun -sdk iphonesimulator clang -arch arm64-rewrite-objc main.m -o main-arm64.cpp Iphoneos clang -arch arm64 - rerewrite -objc main.m -o main-arm64.cppCopy the code
-
Open the compiled main. CPP, find the LGPerson definition, and see that LGPerson is compiled into a struct structure underneath
-
So the first property in LGPerson_IMPL is actually isa, and it inherits from NSObject, and it’s pseudo-inheritance, and the pseudo-inheritance is by directly defining the NSObject structure as the first property in LGPerson, That means LGPerson owns all the member variables in NSObject.
-
The first NSObject_IVARS property in LGPerson is equivalent to isa in NSObject
@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 code
- Clang compilation results, as shown below:
From the above analysis, you understand the nature of the OC object, but looking at the definition of NSObject raises a question: why is isa of type Class?
-
InitInstanceIsa (int int, int int, int int, int int, int int, int int, int int, int int, int int, int int, int int, int int, int int);
-
In NSObject, ISA is defined as Class. The fundamental reason is that ISA sends back Class information. In order to make developers more clear, isa needs to perform a type cast when it returns, similar to the strong cast of AS in Swift. Isa’s strong transformation in the source code is shown below
conclusion
Therefore, it can be concluded from the above exploration process:
The nature of OC objects
In fact, isThe structure of the body
LGPerson
In theisa
Is inherited fromNSObject
In theisa
Objc_setProperty source code exploration
In addition to LGPersong’s underlying definition, we also find set and GET methods for the property name, as shown below, where the implementation of the set method depends on the objc_setProperty in the Runtime.
-
You can follow these steps to unravel the underlying implementation of objc_setProperty
-
Search objc_setProperty globally to find the source implementation of objc_setProperty
Enter the source code implementation of reallySetProperty. The method is based on the new value retain and the old value release
conclusion
After exploring the underlying source code of objc_setProperty, the following points emerge:
objc_setProperty
The purpose of the method applies toCorrelation of the upper
theset
Methods andThe underlying
theset
Method, in essence, is ainterface
- It’s designed that way
why
Is that,The upper
theset
There are many ways ifCall the underlying set directly
Method, it produces a lot of omegaTemporary variable
When you want toTo find the
An SEL is going to be verytrouble
- For these reasons, Apple has adopted it
Adapter design pattern (which ADAPTS the underlying interface to the one the client needs)
.foreign
To provide ainterface
, which is used by the upper-level set method,internal
Calling the underlyingSet method
, so that they are not affected by each other, i.eNo matter what happens at the top, the bottom stays the same, right
Or,What happens at the bottom doesn't affect what happens at the top
To isolate the upper and lower interfaces
The following figure shows the relationship between the top layer, the isolation layer, and the bottom layer
How CLS relates to classes
Alloc, init, and new: alloc, init, and new: alloc, init, and new
Before we do that, we need to know what isa union and why is isa’s type isa_t defined as a union
Union
There are two ways to construct data types:
The structure of the body
(struct
)A consortium
(union
, also known asThe appropriate
)
Structure STRUCTURE refers to the combination of different data into a whole, the variables are coexisting, regardless of whether the variables are used, the allocation of memory.
- Disadvantages: All attributes are allocated memory, compare
Waste of memory
, suppose there are four int members allocated16
Bytes of memory, but when used, you only use it4
Bytes, the rest12
Bytes are a waste of memory - Pros: Storage
Capacity is larger
.receptive
And between membersThey don't affect each other
A consortium
Union is also composed of different data types, but its variables are mutually exclusive, and all the members occupy a segment of memory. Moreover, the common body adopts the memory overwrite 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
- Weakness: Weak tolerance
- Advantages: All members share a segment of memory, which makes memory usage more delicate and flexible and saves memory space
The difference between the two
-
Memory usage
- Structure of the
Each member occupies different memory
Between each otherHave no effect on
- Share body
All members occupy the same memory segment
, modify a member willimpact
All remaining members
- Structure of the
-
Memory allocation size
- Structure in vivo
> =
All members occupiedThe 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 isa pointer type ISA_t, which is defined by union.
The reason for the use of union for the ISA_t type is also based on memory optimization, which is implemented in the ISA pointer through the principle of char + bitfield (that is, each bit in binary can represent different information). Typically, isa Pointers take up 8 bytes of memory, or 64 bits, which is enough to store a lot of information and can result in significant memory savings to improve performance
It can be seen from the definition of ISA_t:
-
It provides two members, CLS and bits, which are mutually exclusive, as defined by the union, meaning that there are two ways to initialize isa Pointers
- through
cls
Initialization,Bits No default value
- through
bits
Initialization,CLS has default values
- through
-
The constructor member ISA_BITFIELD, which is a macro definition with two versions __arm64__ (for ios mobile) and __x86_64__ (for macOS). Here are some of their macro definitions. As shown in the figure below
-
-
Nonpointer Specifies whether to enable pointer optimization for isa Pointers. It has two values, representing custom classes and so on
0
:Pure isa pointer
1
: not onlyClass object address
Isa containsThe class information
And the object’sReference counting
Etc.
-
Has_assoc represents the associated object flag bit, which is 1 bit
0
:There is no relationship
object1
:A link between
object
-
Has_cxx_dtor indicates whether the object has a C++/OC destructor (similar to dealloc), with 1 bit
- if
There are
Destructor, is requiredDo the destructor
logic - if
There is no
, can be fasterThe release of
object
- if
-
Shiftcls represents the value of the pointer to the stored class (the address of the class), that is, the class information
arm64
A majority of the33
Bit to turn on pointer optimization in case of arm64 architecture33
Bits are used to store class Pointersx86_64
A majority of the44
position
-
Magic is used by the debugger to determine whether the current object is a real object or not initialized. It has six bits
-
Weakly_refrenced is a weak variable of whether an object is or has been pointed to an ARC
- Objects without weak references can be released faster
-
The DealLocating object indicates whether memory is being freed
-
Has_sidetable_rc indicates that when the object reference count is greater than 10, the variable is borrowed to store carry
-
Extra_rc (Extra reference count), which represents the reference count value of this object, is actually the reference count value minus 1
- If the object’s
The reference count is 10
, thenextra_rc
9 (this is for example only), which is actually true for the iPhoneextra_rc
It uses 19 bits to store reference counts
- If the object’s
-
For the two different platforms, the isa storage situation is shown in the figure
The principle of exploring
- through
alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone
Method path, found toinitInstanceIsa
And into its principle of implementation
Enter the source implementation of the initIsa method, primarily to initialize the ISA pointer
The logic of this method is divided into two parts
- through
cls
Initialize theisa
- through
bits
Initialize theisa
Verifying isa pointer bit fields (0-64)
From the 0-64 bit fields mentioned above, you can prove here that isa pointer has these bit fields in the initIsa method (currently on macOS, so x86_64 is used).
Init with the LGPerson breakpoint in main –> initInstanceIsa –> initIsa –> go to ISA in else
To continue, go to newisa.bits = ISA_MAGIC_VALUE; On the next line, assign a value to the bits member of ISA and re-execute the LLDB command p newisa. The result is as follows
Magic is 59 because the ISA pointer address is converted into binary, 6 bits are read from 47 (because there are 4 bit fields in front, 47 bits are occupied, and the address starts from 0), and then converted into decimal, as shown in the figure below
Isa’s association with classes
The principle of CLS association with ISA is that the shiftCls bit field in ISA pointer stores the class information, where initInstanceIsa process is to associate the Calloc pointer with the current CLS class. There are the following verification methods:
- [Method 1] Through
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 - [Mode 3] Through the Runtime method
object_getClass
validation - [Mode 4] Pass
An operation
validation
Method 1: Use the initIsa method
Run the preceding shiftcls = (uintptr_t)newCls >> 3, where Shiftcls stores the value information of the current class
- At this point to see
cls
, it isLGPerson
class shiftcls
The logic of assignment is to assignLGPerson
After coding,Moves to the right. 3
position
Execute the LLDB command p (Uintptr_t) CLS and the result is (uintptr_t) $8 = 4295000992. Move it three places right in either of the following ways to get 536875124 and store it in shiftcls of Newisa
p (uintptr_t)newCls >> 3
- Through the results of the previous step
$8
Run the LLDB commandp $8 >> 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
由The default value
And turned intoLGPerson
That will beIsa and CLS are perfectly correlated
shiftcls
by0
Turned out to be536875124
So isa changes the value 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
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
- After method one, proceed to return
_class_createInstanceFromZone
Method, at this pointCLS and ISA have been associated
, the implementation ofpo objc
- perform
x/4gx obj
,isa
Address of pointer0x011d8001000083a1
The ISA pointer address & ISA_MASK (in macOS, defined using macros in X86_64), i.e. Po 0x011D8001000083a1&0x00007ffffFFFFff8, gives LGPerson
arm64
, the value defined by the ISA_MASK macro is0x0000000ffffffff8ULL
x86_64
, the value defined by the ISA_MASK macro is0x00007ffffffffff8ULL
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:
- #import
- through
runtime
API, namelyobject_getClass
Function to get class information
object_getClass(<#id _Nullable obj#>)
Copy the code
- To view
object_getClass
Function source code implementation
- Enter the
getIsa
Source code implementation
- Click on the
ISA()
, enter the source code
- Click on the
getDecodedClass
, enter the source code
- Click on the
getClass
, enter the source code
- in
else
In the process, getisa
thebits
This place, and then& ISA_MASK
, which is consistent with the principle in mode two,Get the current class information
- You can do it from here
CLS and ISA have been perfectly correlated
Mode 4: Through 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 of being inmacOS
Environment)
If you want to read the 44-bit information in the middle, you need to go through the bit operation, erase the three bits on the right and the part on the left except the 44-bit, and its relative position is unchanged. Its bit operation process is shown in the figure, where Shiftcls is the class information to be read
-
Move the ISA address right by 3 bits: P /x 0x011D8001000083A1 >> 3 to get 0x0023B00020001074
-
In the obtained 0x0023B00020001074, shift 20 bits left: P /x 0x0045138768236660 << 20, get 0x0002000107400000
-
Why 20 to the left? Because we moved 3 to the right, we moved 3 to the right, and we had 17 zeroes to erase, so we had to move 20
-
Move the resulting 0x0002000107400000 17 bits right: P /x 0x0002000107400000 >> 17 to get the new 0x00000001000083A0
-
P /x CLS also produces 0x00000001000083A0, so it can be proved that CLS is associated with ISA