“This is the third day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.
- This article mainly introduces the life cycle analysis of classes
We know that in iOS, app development compiles our code into machine-recognized code mach-O files through the compiler, which is divided into front-end compiler and back-end compiler. The front-end compiler is CLang in OC, and the SWIFT compiler is Swift. Through the front-end compiler for lexical analysis, syntax analysis, check whether the syntax is correct to generate intermediate code IR, code optimization through the middle layer, to the back-end LLVM for processing, generate Mach-O file
The process is as follows:
You can refer to my previous article for the specific processLLVM process
Let’s take a look at the command for the front-end compiler swift, similar to the command statement for clang in oc
Swiftc main. Swif-dump-parse // Analyze and check the type of output AST swiftc main. Swif-dump-ast // generate intermediate language (SIL), Swiftc main. Swift - EMIT - Silgen // Generate intermediate Language (SIL) Swift-emit -sil // Generates LLVM intermediate language (.ll file) swifTC main. Swift-emit -ir // generates LLVM intermediate language (.bc file) swifTC Swift -emit-assembly // Compilation generates executable. Out file swiftc-o main.o main.swiftCopy the code
1. SIL file analysis
Create a new CommandLineTool project and define a Person class in main
The following code is generated by typing at the terminalsil
The file is generated and printed at the terminalmain.sil
file
swiftc main.swift -emit-sil
Copy the code
The person above is our class, with the get and set methods for attributes, and the deinit and init methods. @main is the entry function.
We can generate the main.sil file in the current directory by using >
swiftc main.swift -emit-sil > ./main.sil
Copy the code
// main is equivalent to the entrance of main in oc sil@main: $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): alloc_global @$s4main1pAA6PersonCvp // id: %2 Assign a person global variable %3 = global_addr @$s4main1pAA6PersonCvp: $* person // user: %4 = metatype $@thick person. Type // user: %6 %4 Assign a meta Type to person.type // function_ref person.__allocating_init ()// execute the init method function %5 = function_ref @$s4main6PersonCACycfC : $@convention(method) (@thick Person.Type) -> @owned Person // user: %6 = apply %5(%4) : $@convention(method) (@thick person.type) -> @owned Person // user: %6 store %6 to %3: $*Person // id: Person %8 = integer_literal $builtin.int32, 0 // Person %8 = integer_literal $builtin.int32, 0 // user: Struct $Int32 (% 2: $builtin.int32) // struct $Int32 (% 2: $builtin.int32) // struct $Int32 (% 3: $builtin.int32) // struct $Int32 (% 3: $builtin.int32) // $Int32 // id: %10 } // end sil function 'main'Copy the code
The above %0, %1, etc are virtual registers. In the SIL file, you’ll see a lot of keywords you don’t understand. You can check the official documentation on GitHub
- Script compilation open
We can also add script to run open 1. Add target
addRun the script
Add the script in the folder where main is currently located
Swiftc - emit - ${SRCROOT} sil project folder / * * / main. Swift | xcrun swift - demangle >. / main sil && open main. SilCopy the code
If you get an error, or you can’t open it, you can open it firstvscode
To set upmain.sil
The opening mode of
Compile analysis process
We use assembly for analysis
We break the point before the Person class is initialized, and then in__allocating_init()
At break point
Setp into the current method, hold down control and click enter
Once inside, the init method is called to initialize it
Enter the init
3. Source code analysis
Let’s take a look at the source code. Source code can go to apple’s official website -swift source download address. Use VSCode to open the downloaded Swift source code and search swift_allocObject globally.
Just like the implementation of _swift_allocObject_, we pass three parameters, create memory space with swift_slowAlloc based on the allocated memory size, then associate the metadata with the memory space, and finally return the instance object.
Click swift_slowAlloc to view
Alignment Default size
Above we end up generating an object of heapObject, just like I did in OC class inheritance with objC_class
- HeapObject
There are two main initialization methods, each containing two parameters: HeapMetadata metaData and InlineRefCounts refcounts
HeapMetadata
Check the TargetHeapMetadata
It is compatible with classes in OC, and the data element is ISA. The swift class defaults to MetaDataKind data
To view#include "MetadataKind.def"
Get the corresponding value
name Value Class 0x0 Struct 0x200 Enum 0x201 Optional 0x202 ForeignClass 0x203 ForeignClass 0x203 Opaque 0x300 Tuple 0x301 Function 0x302 Existential 0x303 Metatype 0x304 ObjCClassWrapper 0x305 ExistentialMetatype 0x306 HeapLocalVariable 0x400 HeapGenericLocalVariable 0x500 ErrorObject 0x501 LastEnumerated 0x7FFCopy the code
Continue to look at TargetMetadata inherited from TargetHeapMetadata, which structures can inherit from in C++
So if you look at TargetMetadata there’s a bunch of method attributes, so let’s focus on the methods of getTypeContextDescriptor
When kind is a Class, we get a pointer to TargetClassMetadata. If kind is Class, we get a pointer to TargetClassMetadata.
Inherit TargetAnyClassMetadata and click to view it
If it is an OC class, the TargetAnyClassMetadata structure is similar to the structure of our OC class isa, superClass, Cache, bits (data).
So let’s summarize the data structure of SWIFT
struct Metadata {
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
Copy the code
From the previous analysis, we know that the initialization generates a heapObject, which we can customize according to this structure
class Person{ var name = "fish" var age = 3 } struct HeapObject{ var metadata: UnsafeRawPointer var refCounts: UInt32 } struct Metadata{ var kind: Int var superClass: Any.Type var cacheData: (Int, Int) var data: Int var classFlags: Int32 var instanceAddressPoint: UInt32 var instanceSize: UInt32 var instanceAlignmentMask: UInt16 var reserved: UInt16 var classSize: UInt32 var classAddressPoint: UInt32 var typeDescriptor: UnsafeMutableRawPointer var iVarDestroyer: UnsafeRawPointer} let p = Person() let objcRawPtr = unmanaged.passunretained (p as AnyObject).toopaque ()// Obtain the pointer let of the instance object objcPtr = objcRawPtr.bindMemory(to: HeapObject.self , capacity: 1)// Convert HeapObject to our custom MetaData structure print(objcptr.pointee);Copy the code
Let’s use LLDB to verify
A metadata class is essentially a structure
4. To summarize
- The nature of the class in Swift is
heapObject
The structure of the type containsmetadata
andReference counting
The nature of the class in oc isobjc_class
Type of structure. - The instance object in Swift stores
metadata
andReference counting
.Member variables
. The instance variable in oc stores isisa
andMember variables
- The swift,
metadata
Contains information about the class throughgetTypeContextDescriptor
Get the type of the description and store the data in the corresponding structure. - A flowchart