Classes and structs in Swift

Posted by DarkShadowWing on Mon, 03 Jan 2022 08:17:32 +0100

Comparison of classes and structures

Classes in swift:

class LLPerson {
    var age: Int
    var name: String
    
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name
    }
}

var person = LLPerson(18, "LL")
var person1 = person


Class is a reference type, person to person1 is the assignment of the pointer, and the contents pointed to by the pointer are consistent.

po: the difference between p and po is that using po will only output the corresponding value, while p will return the type of value and the reference name of the command result.
x/8g: read the value in memory (8g: output in 8-byte format)


The addresses of printed person and person1 are inconsistent, and the stored memory address is 8 bytes.

When creating a person object, allocate 8 bytes in the stack area, then allocate space in the heap area, put the value in the heap area, assign the pointer to person, and the pointer points to heap. You can use cat address xxx to view the pointer.

Structure in swift:

struct LLPerson {
    var age: Int
    var name: String
    
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name
    }
}

var person = LLPerson(18, "LL")
var person1 = person

person1.age = 19
person1.name = "GG"




The structure is a value type. po prints the value. Modifying the value in person1 will not affect person.

For a class, you need to find the value in the heap through the pointer. The structure only needs to move the pointer on the stack.

Main similarities between structure and class:

  1. Define properties that store values
  2. Definition method
  3. Define subscripts to provide access to their values using subscript syntax
  4. Define initializer
  5. Use extension to expand functionality
  6. Follow the protocol to provide a function

Main differences:

  1. Classes have inherited properties, but structs do not
  2. Type conversion enables you to check and interpret the types of class instances at run time
  3. Class has a destructor to free its allocated resources
  4. Reference counting allows multiple references to a class instance

Can pass github upper StructVsClassPerformance This case is used to intuitively test the time allocation of the current structure and class.

Class initializer



The current class compiler does not automatically provide member initializers by default, but for structures, the compiler will provide default initialization methods (provided that we do not specify initializers ourselves)!

class LLPerson {
    var age: Int
    var name: String
    
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name
    }

    convenience init() {
        self.init(18, "LL")
    }
}

When creating instances of classes and structures in Swift, you must set an appropriate initial value for all storage properties. Therefore, the class LLPerson must provide the corresponding specified initializer. At the same time, we can also provide a convenient initializer for the current class (Note: the convenient initializer must call another initializer from the same class).

The portable initializer must call other initializers first, otherwise it will fail to compile and report an error. The property must be initialized when the property is accessed. For safety reasons.

Initializer rule:

  1. The specified initializer must ensure that all properties introduced by its class are initialized before delegating to the parent class initializer.
  2. The specified initializer must delegate the parent class initializer up before setting new values for inherited properties. If this is not done, the new value given by the specified initializer will be overwritten by the initializer in the parent class.
  3. The convenience initializer must first delegate other initializers in the same class, and then assign new values to any property (including the properties defined in the same class). If this is not done, the new values assigned by the convenience initializer will be overwritten by other specified initializers in its own class.
  4. Before the first stage initialization is completed, the initializer cannot call any instance method, read the value of any instance property, or reference self as a value.

Failable initializer:

class LLPerson {
    var age: Int
    var name: String
    
    init?(_ age: Int, _ name: String) {
        if age < 18 {return nil}  
        self.age = age
        self.name = name
    }
    
    convenience init?(_ age: Int) {
        self.init(18, "LL")
    }
}

In this Swift, the failable initializer writes a return nil statement to indicate under what circumstances the failable initializer will trigger an initialization failure.

Necessary initializers:

Add the required modifier before the class initializer to indicate that all subclasses of the class must implement the initializer.

Class lifecycle

Swift is compiled into IR by swift compiler, and then generates executable files.

Analysis output AST
swiftc main.swift -dump-parse

//Analyze and check the type output AST
swiftc main.swift -dump-ast

Generated intermediate language (SIL), not optimized
swiftc main.swift -emit-silgen

Generated intermediate language (SIL), optimized
swiftc main.swift -emit-sil

Generate LLVM intermediate language (. ll file)
swiftc main.swift -emit-ir

Generate LLVM intermediate language (. bc file)
swiftc main.swift -emit-bc

Generate assembly
swiftc main.swift -emit-assembly

Compile to generate executable out file
swiftc -o main.o main.swift

You can use swiftc - emit SIL ${srcroot} / llswifttest / main swift > ./ main. sil & open main. SIL generate SIL file:

class LLPerson {
    var age: Int = 18
    var name: String = "LL"
}
class LLPerson {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

SLL files can be referenced https://github.com/apple/swift/blob/main/docs/SLL.rst.
Swift object memory allocation:

__allocating_init -----> swift_allocObject -----> swift_allocObject -----> swift_slowAlloc -----> Malloc

The memory structure HeapObject (OC objc_object) of Swift object has two attributes: Metadata and RefCount. By default, it occupies 16 bytes.
After accessing the source code, the following structure can be obtained:

Class structure:

struct HeapObject { //Custom class structure
    var metaData: UnsafeRawPointer
    var refcounted1: UInt32
    var refcounted2: UInt32
}

class LLPerson {
    var age: Int = 18
    var name: String = "LL"
}

var person = LLPerson()

let objcRawPtr = Unmanaged.passUnretained(person as AnyObject).toOpaque() //Get object pointer
let objcPtr = objcRawPtr.bindMemory(to: HeapObject.self, capacity: 1)  //Rebind
print(objcPtr.pointee)

metadata structure:

struct Metadata {  //metadata structure
    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 metadata = objcPtr.pointee.metaData.bindMemory(to: Metadata.self, capacity: MemoryLayout<Metadata>.stride).pointee
print(metadata)

Topics: Swift iOS