This is the fifth day of my participation in Gwen Challenge
The introduction
- We already know about iOS objects
+alloc
Method bottom layerInstanceSize (Memory allocation)
.InitInstanceIsa (binding type)
Operations such as memory allocation and computation-related information can be passedIOS Basic Exploration 01 — ALLOc preliminary ExplorationandIOS Basics 02 — Structure alignment PrinciplesConduct a review; - This article will continue to examine the core methods of the alloc method
InitInstanceIsa (binding type)
To explore;
First, the nature of the object
LLVM 1.1 and GCC
- We all know OC is a superset of C language. In order to explore the nature of OC objects, the best way is to see the implementation of OC objects in the underlying language. Early OC compilers used
GCC compiler
, butGCC compiler
The front end and back end of the compiler are over-coupled when extended language support is requiredThe CPU architecture
When not only to add compiler back-end functions, front-end code also need to increase, very troublesome; - In order to solve the
GCC compiler
Front – and back-end coupling is bad for scaling, which Apple introducedThe LLVM compiler
Because ofThe LLVM compiler
The front end and back end are isolated, and only need to modify the compiler back end when extending the architecture supported by the language, which has higher scalability and better performance at this stageXcode
The built-in compiler has been replaced with LLVM; - Here we use
The LLVM compiler
A front-end compiler in the frameworkclang
theOC
Code restore toc++
;
1.2 Clang rewrite OC
There are two ways of reduction, namely:
- Clang directly rewrite
Clang-rewrite-objc-fobjc-arc-fobjc-runtime = ios-14.2.0-isysroot / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator14.2. The SDK main.mCopy the code
- Rewrite using Clang in XCRun
Xcrun - 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
Either way, we can rewrite main.m to main. CPP. Once we’re done, we can look at the underlying C++ file.
Before rewriting
rewritten
1.3 Read the rewritten OC code
- Found by keyword search
OC object GCPerson
Is converted toGCPerson structure
; GCPerson structure
Internally holding aNSObject_IMPL structure
Type of a member variableNSObject_IVARS
andNSString
Types of gcName;- Finally it can be determined
OC object GCPerson
The bottom line is a variable that has only one memberisa
theNSObject_IMPL structure
And all inherits fromNSObject
theOC object
A structure that the underlying implementation hasNSObject_IMPL
Struct GCPerson_IMPL {GCPerson struct NSObject_IMPL NSObject_IVARS; NSString *__strong _gcName; }; Struct NSObject_IMPL {// Struct NSObject_IMPL __unsafe_unretained Class isa; struct NSObject_IMPL __unretained Class isa; };Copy the code
- And we also found alpha and beta
id
andClass
Some underlying implementation information of
typedef struct objc_object GCPerson; // rename objc_object to GCPerson typedef struct objc_class *Class; // The pointer to the objc_class structure is aliked as Class typedef struct objc_object *id; // objc_object structure pointer alias bit IDCopy the code
The *Class and * ID aliases already contain Pointers to *, so we can use id and Class without adding *.
- In addition, we can find the compiler to help us
OC object GCPerson
The properties of thegcName
addedset
Methods andget
Methods; Method is addedGCPerson
The type ofself
andSEL
The type ofcmd
These two parameters;
Static void _I_GCPerson_setGcName_(GCPerson * self, SEL _cmd, NSString *gcName) { (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_GCPerson$_gcName)) = gcName; } //get static NSString * _I_GCPerson_gcName(GCPerson * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_GCPerson$_gcName)); }Copy the code
- through
gcName
theget
The method can be seen: obtaingcName
The member method isself
The address of the +OBJC_IVAR
Address, and then strong intoOC
theNSString
Can; Why is that?self
The address is the first address of the current object, willivar
The next place after the skip is the member variablegcName
The address; As shown in the figure below;
7. Continue reading the source code, we find other information in the underlying layer, such as_protocol_t
.ivar
.methodlist
.category_t
.class_t
And other relevant information;
Second, the nonPointerIsa
2.1 Structure, union, bitfield
Before we explore the nonPointerIsa method, here are a few things to know
2.1.1 structure
Struct is a storage structure, characterized by “coexistence” of all member variables, with the advantage of “large tolerance” and more comprehensive information. The disadvantage is that the allocation of struct memory space is extensive, regardless of use, all allocation, not fine;
2.1.2 consortium
Union is also a kind of storage structure, characterized by the variable is “mutually exclusive”, the advantage is more precise and flexible memory use, but also saves memory space; The disadvantage is that it is not “inclusive” enough. The use of override technology leads to the internal member variables covering each other, and only one member variable value can be stored at the same time. Age overwrites name, as shown below
Sets the union member variablesname
Sets the union member variablesage
2.1.3 a domain
A domain
Structure is a storage technique commonly used in structures. generally, the information of a structure is accessed in bytes, but sometimes only a few bytes of information are storedByte an
B: Yes, it can be used in this caseA domain
Storage technology; As is shown in
- For example: a
BOOL
Take upA byte is 8 bits
, fourBOOL
It’s 32 bits, but actuallyBOOL
with0
and1
That means you only need onebyte
Bit can be stored, fourBOOL
With fourByte an
Half a byte is enough; A domain
Although it can save storage space, it is not very convenient to use bitfields in daily development under current hardware conditions because of the need to operate specific byte bits when reading and writing.- Bit fields are read and written with help
mask
To conduct, so calledmask
It’s a particular set ofByte an
Information through willThe original data
withmask
forThe bitwise and &
orBitwise or |
Operation, you can read and write the value of the bitfield;
2.1.4 Bit-field and consortium are used together
In general, bit-domain and consortium are used together to save storage space.
2.2 initInstanceIsa
2.2.1 isa_t
After analyzing the core methods instanceSize and calloc during alloc, we now move on to the other core method initInstanceIsa, where we can see a key message isa_t
inline void objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor) { ASSERT(! isTaggedPointer()); // Check whether TaggedPointer isa_t newisa(0); if (! nonpointer) { newisa.setClass(cls, this); } else { ASSERT(! DisableNonpointerIsa); ASSERT(! cls->instancesRequireRawIsa()); #if SUPPORT_INDEXED_ISA ASSERT(cls->classArrayIndex() > 0); newisa.bits = ISA_INDEX_MAGIC_VALUE; // isa.magic is part of ISA_MAGIC_VALUE // isa.nonpointer is part of ISA_MAGIC_VALUE newisa.has_cxx_dtor = hasCxxDtor; newisa.indexcls = (uintptr_t)cls->classArrayIndex(); #else newisa.bits = ISA_MAGIC_VALUE; // isa.magic is part of ISA_MAGIC_VALUE // isa.nonpointer is part of ISA_MAGIC_VALUE # if ISA_HAS_CXX_DTOR_BIT newisa.has_cxx_dtor = hasCxxDtor; # endif newisa.setClass(cls, this); #endif newisa.extra_rc = 1; } isa = newisa; }Copy the code
The brief structure of ISA_t is as follows. In order not to affect the analysis, I have deleted some irrelevant information.
Union isa_t {isa_t() {} uintptr_t () : bits(value) {} private: Class cls; public: #if defined(ISA_BITFIELD) struct { ISA_BITFIELD; Defined in isa.h}; . Irrelevant content};Copy the code
The member variable ISA_BITFIELD can be seen from the isa_T structure. The truth is found in the interface of defining ISA_BITFIELD.
2.2.2 ISA_BITFIELD
ISA_BITFIELD is a structure that uses bit-domain storage technology. The different byte bits inside the structure store information as follows
// Storage information of each byte in the arm64 real machine
# if __arm64__
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t unused : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
# endif
// Storage information of each byte in the _x86_64 schema
# elif __x86_64__
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t unused : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8
Copy the code
ISA_BITFIELD
inbyte
The distribution of bits is shown below, with each color representing the amount of space required to store a member variableByte an
The number ofMember variable occupancyByte an
The locations and uses of member variables are shown in the table below
Member variables | Under the arm position | Under the x86_64 position | use |
---|---|---|---|
nonpointer |
63 | 63 | Is it true or notisa Pointer on pointer optimization 0:Pure isa Pointer, 1: not only class object address,isa Contains class information, object reference counts, and so on |
has_assoc |
62 | 62 | Associated object flag bit, 0 absent, 1 present |
has_cxx_dtor |
61 | 61 | Whether the object hasC++ or Objc If there is a destructor, it needs to do the destructor logic, if there is no, it can be faster to free the object |
shiftcls |
28 and 60 | 17-60 | Store the value of a class pointer. Turn on pointer optimization in case ofarm64 There are 33 bits in the schema to store class Pointers; inX86_64 The schema has 44 bits to store class Pointers |
magic |
22-27 | 11-16 | Used by the debugger to determine whether the current object is a real object or has no space to initialize |
weakly_referenced |
21 | 10 | Weak variables that indicate whether an object is or has been referred to an ARC. Objects without weak references can be released faster |
unused |
20 | 9 | Flags whether the object is freeing memory |
has_sidetable_rc |
19 | 8 | When the object reference technique is greater than 10, the variable is borrowed to store the carry |
extra_rc |
0 to 18 | 0 to 7 | When representing the reference count value of this object, the reference count value is actually reduced by 1. For example, if the object’s reference count is 10, extra_rc is 9. If the reference count is greater than 10, the following is used has_sidetable_rc . |
2.3 Read Mode of Shiftcls 1
- We already know that
ISA_BITFIELD
For bit-domain storage,shiftcls
Read can be used as a member variable in a bit-domain structureISA_BITFIELD
&ISA_MASK
The way; - in
person
After the object is initialized, thex/4gx person
To viewperson
The object’sISA_BITFIELD
Information; - Select an appropriate device based on the current device architecture
ISA_MASK
withISA_BITFIELD
for&(bitwise and)
Action to get the current objectClass object
Address to point to:p/x 0x000001a10405d685 & 0x0000000ffffffff8ULL
; - through
p/x person.class
Operation viewperson
Object directedClass object
Address; - Found in Step 3 and Step 4
Class object
The address is consistent, which proves that reading mode 1 is no problem;
2.4 Read Mode of Shiftcls 2
When we analyze the system’s initIsa method, we find that the actual method of binding Class is newisa.setClass(CLS, this), which is implemented as follows
inline void
isa_t::setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_NONE
uintptr_t signedCls = (uintptr_t)newCls;
# elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ONLY_SWIFT
uintptr_t signedCls = (uintptr_t)newCls;
if (newCls->isSwiftStable())
signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));
# elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL
uintptr_t signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));
# else
# error Unknown isa signing mode.
# endif
shiftcls_and_sig = signedCls >> 3;
#elif SUPPORT_INDEXED_ISA
cls = newCls;
#else
shiftcls = (uintptr_t)newCls >> 3;
#endif
}
Copy the code
This method essentially shifts the ISA_BITFIELD; Let’s use a real machine with the ARM64 architecture to demonstrate the above code shift operation.
- because
ISA_BITFIELD
The fixed bit in isshiftcls
, so we need to move the irrelevant ones through the displacement operationByte an
The message is moved out of 64 bits so that what is left isshiftcls
Bits of information; - First turn on the
shiftcls
We’re going to move 3 to the rightshiftcls
All the rightByte an
Empty; - Then put the
shiftcls
We’re going to move 3+28 to the leftshiftcls
Doing allByte an
Empty; - After the operations in Step 3 and Step 4 are performed
ISA_BITFIELD
There’s only one leftshiftcls
Information, but after the shiftshiftcls
I have to move it to the right28
Bit back to original position; - Found in Step 3 and Step 4
Class object
The same address proves that reading mode 2 is no problem;
Init, new analysis
- The object’s
init
The factory design pattern () method provides a constructor for the factory design pattern, which is used to extend the subclass for easy overwrite initialization. new
Methods are also called internallyinit
Method,New is essentially alloc combined with new
3. Compile and viewalloc
Will be calledobjc_alloc_init
, butnew
Call isobjc_opt_new
, upon inspectionobjc
Source code found that the implementation of the two methods is the same; soAlloc, init
The initialization mode andnew
The initialization method can be considered consistent;