1. The difference between classes and structures

Similarities:

  • Define member properties, methods
  • Define subscripts to provide access to their values using subscript syntax
  • Define initializers
  • You can use Extension to extend the functionality
  • Follow a protocol to provide a function

Difference:

  • Classes can inherit
  • Type conversions enable you to check and interpret the type of a class instance at run time
  • A class has a destructor that frees its allocated resources
  • Reference functions allow multiple references to a class instance

Conclusion:

  • Classes are reference types. That is, a variable of class type does not store an instance object directly, but rather a reference to the memory address where the instance is currently stored (the contents of the instance can be changed through address modification). Structs, on the other hand, are value types that store concrete values, but a set of copied values (changing values does not affect the original concrete instance). Simply put, for a concrete instance, the class is a pointer, and the structure is a copy of its value (enumerations are also value types).

    • Po the following classes:

    • Po structure:

    As you can see, you just change the type of LZPerson and print out a pointer and a concrete value

  • Four commands are briefly introduced:

    • poandp: ppoThe difference is in usePo prints only the corresponding valueAnd theP returns the type of the value and the reference name of the command result.
    • x/8g: Reads the value in memory (8G: output in 8-byte format)
    • withUnsafePointer(to: T, body: (UnsafePointer<T>) throws -> Result): Certain types of Pointers are defined in Swift, and each type has its purpose and function. Using the appropriate pointer type can prevent errors and more clearly express the developer’s intent to prevent undefined behavior. The name of the pointer type tells us what type of pointer it is:Variable/immutable,Raw/Typed,Buffer type, there are generally the following 8 types:
      • Unsafe, isn’t it
      • Write Access: Write is available
      • Collection: Acts like a container for adding data
      • Strideable: Pointers can be moved using the advanced function
      • Typed: Whether the type needs to be specified.

    Digress… Print the values of t and t1 to see the effect:

    • frame variable: Displays the memory layout of a variable according to a rulehelp frame variableTo view
      // After the breakpoint, run the frame variable-l command in LLDBCopy the code

  • Another big difference between reference types and value types is where they are stored: typically, reference types are stored on the heap, while value types are stored on the stack

    • Stack: The context in which local variables and functions are run

      • Compile time determination, it isstaticIs assigned by the system, soSpeed is fast
      • The stack expands from a high address to a low address
      • Function calls are on the stack, and the stack data in different functions cannot be called by another function. In multithreading, each thread is started by calling a function, so each thread has its own stack
      • Stack is used when data size can be determined because it is efficient and fast
    • Heap: Stores all objects

      • Runtime determination, it isdynamic, assigned by the programmer, soSlow speed
      • The heap grows from a low address to a high address
      • The heap is in a process, and all threads in the process have access to the heap data
      • Use heap when data size is uncertain; Use heap when working with very large amounts of data, because it needs to be released as soon as possible
    • Global: Stores global variables, constants, and code areas

    • Digress again.. Ok, so we can use structs and enumerations as much as possible in our code, because it’s more efficient

1.1. Initializer

  • Swift is type-safe and requires assignment to member attributes of the created class and structure. Non-assignment requires the creation of an initializer for the classInitializers need to be created manuallyAnd theThe structure system creates an initializer by default
1.1.1. Specify an initializer
  • No assignment case, self-defined initializer

  • Inherited class, according toThe newly added property initializer -> calls the initializer of the parent super -> reassigns the inherited propertyThe order of

1.1.2. Convenient initializer
  • useconvenienceModify the init; By preinitialization, toImplement missing argument initializer class or structure, so the convenience initializer must be configured fromSame classCall another initializer to preinitialize first;Until the pre-initialization is complete, the call is limited and even self cannot be used
1.1.3 Failable initializer
  • Add optional identifier after init?; The initialization fails because the parameters are invalid or external conditions are not met.Filter the input parametersAnd be aware that,If the specified initializer is optional, then convenient initialization must also be optional
1.1.4 Necessary initializers
  • In front of the class initializerrequiredModifier to indicate that all subclasses of the class must implement the initializer

2. Class lifecycle

  • IOS development languages, whether OC or Swift, are passedLLVMCompiled, and finally generated.ofile
  • OCthroughclangCompiler, compiles toIR, and then regenerate it into an executable file.o(Here is our machine code)
  • Swift is compiled into IR by Swift compiler and then generates executable files

2.1 Swift compilation process

  1. Swift Code is parse (abstract syntax tree) through -dump-parse semantic analysis

  2. Parse Performs semantic analysis using -dump-ast to check whether the syntax is correct and secure

  3. Seam then demotes Swift Code to SILGen(Swift Intermediate Language), which is divided into Raw SIL(native, with no optimization option turned on) and SIL Opt Canonical SIL(optimized)

  4. The optimized SIL is demoted from LLVM to IR, which is then compiled into machine code by back-end code

The command is as follows:

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
  • In contrast to OC,Swift code was added during compilationSILFor more stringent security checks on code, as shown below, becauseint8_tThere is only one byte, the result of the operation has been overflowed,OC prints the error data, and Swift reports the error directly

2.2. Look at SIL intermediate code

  • Let’s look at the simple onesSILFamiliarize yourself with the code. Throw a brick

  • To look at our own SIL code, we need to get the SIL file, create a JS script and run it to generate (File –> New –> target)

    • swiftc:Swift compiler; Following is the path and name of the file to be compiled
  • Analyze it with this simple code:

    • Define the class

      If the source Swift code removes the instantiation call and instead addsprint(“end”), get the followingSILI have tried my best to understand it from my own point of view, which may not be quite in place. For a better understanding, I still need to refer to the official SIL document below

    • __allocating_init(): in this paper for__allocating_init(LZAge:_:_isMale), creates an instance object, which the function needsLZPerson.TypeThe element type

    • Alloc_ref creates the corresponding instance variable, whose reference count is initialized to 1; Alloc_ref is essentially going to the heap and asking for memory; The Swift class identified as objC will use objective-C’s +allocWithZone: initialization method

SIL instruction
  • @main: entry function

  • %0, %1, %2… : registers, similar to constants, cannot be changed once assigned, but can only be assigned by adding corner markers, addresses can be assigned by store, virtual, running on the real machine to use real registers

  • @ $: is followed by the mixed name, which can be passed at the terminalxcrun swift-demangleMethod analysis

  • Integer_literal: Creates a system-built integer type

  • Tuple_extract: Of the same type as the element extracted from a tuple

    %1 = tuple_extract %0 : $(T...) , 123 // %0 must be of a loadable tuple type $(T...) // %1 will be of the type of the selected element of %0Copy the code
  • Separator :terminator: print(_:separator:terminator:): is a global function that prints one or more values to the appropriate output area

  • Begin_access: begins to access the target memory.

    • vtable: virtual table,Target records that can be dynamically invoked are tracked in a virtual table
    • SIL stands for usingclass_method,super_method,objc_methodobjc_super_methodinstructionDynamic scheduling of class methods.
  • Potential targets for class_method and super_method are tracked in the SIL_vtable declaration for each class type. This declaration contains the mapping from each method of the class (including methods inherited from its base class) to the SIL function that implements the method of the class:

2.3 Swift Object memory allocation process:

  • To Swift code interrupt point, go assembly debugging

  • Heapobject.cpp:

    • swift_allocObject
    • swift_slowAlloc: Execute malloc to clear up memory space
Summary of memory allocation process
  • __allocating_init(generated by the compiler for each instance object) –> swift_allocObject –> _swift_allocObject_(private function) –> swift_slowAlloc –> Malloc

  • The memory structure of a Swift object is HeapObject(objc_object in OC) and has two properties:

    • Metadata
    • RefCount
    • So default occupancy16-byte size(OC objc_object is isa, so 8 bytes)
      objc_object{ 
          isa 
      }
      Copy the code

3. Swift object memory structure

  • We’ve already mentioned two properties, so let’s go ahead and analyze themMetadataWhat is it

  • See the isHeapMetadataType, continue to jump to see

  • The originalHeapMetadataTargetHeapMetadataAlias for this type, then analyze againTargetHeapMetadata

  • TargetHeapMetadataInherited fromTargetMetadata.MetadataKindIt’s just an INT32 that can’t parse anything, so I can’t go up to the parentTargetMetadatacode

  • In this function, the current class type is distinguished by the type returned by getKind; TargetClassMetadata is the final base class for all types of metaclasses;

    • If it isClass, then the current pointer will bethisthroughstatic_castStrong toTargetClassMetadataType;
    • TargetClassMetadataInherited fromTargetAnyClassMetadata;
    • TargetAnyClassMetadataThe data structure containsSuperclass.CacheData[2].DataAnd so on, which is related toOCThe structure of the class is similar
  • Conclusion: Metadata is basically equivalent to ISA in OC, and its memory structure is:

    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

Swift method expansion

  • Unmanaged. PassUnretained (instance as AnyObject).toopaque (): Gets a pointer to the instance object
  • objcRawPtr.bindMemory(to: *T.type*, capacity: *Int*): Bind memory
  • .pointee: Access pointer