Generic function

func swapTwoValues<T>(_ value1:inout T ,_ value2:inout T){
    let temp = value1
    value1 = value2
    value2 = temp
}
var x = "str1"
var y = "Str2"
swapTwoValues(&x, &y)
Copy the code

The generic type

// The simplest implementation, Int struct Stack{private var items = [Int]() mutating func push(_ item:Int){items. Append (item)} Stack<T>{ private var items = [T]() mutating func push(_ item:T){ items.append(item) } }Copy the code

Type constraints

Syntax: When a parameter type is defined, a separate class or protocol constraint is placed after the parameter name, separated from the parameter name by a colon:. Note: Type constraints can only be classes or protocols.

Func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {} func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {}Copy the code

To place a protocol or class after a type parameter, as requiredType parameter TfollowXX agreementThe compiler reported an error when the diagram User did not comply with the Equatable protocol

Association types

When defining a protocol, use an association type as a placeholder for the type in the protocol

Use of association types

Protocol StackProtocal{associatedType Item mutating func append(_ Item :Item)} struct IntStack:StackProtocal{ var items = [Int]() typealias Item = Int mutating func append(_ item: Int) {items.append(item)}} // Struct ElementStack<T>:StackProtocal{var items = [T]() typeAlias item = T mutating func append(_ item: T) { items.append(item) } } var s = IntStack() s.append(1) print(s.items)Copy the code

Storage of non-generic protocols

protocol Drawable{ func draw() } struct Point:Drawable{ var x:Double func draw() { print("Point") } } struct Line:Drawable{var x:Int func draw() {print("Line")}} // The actual type of mm is a special compiler-generated data type called let mm:Drawable = arc4random()%2 == 0 ? Point (x: 20.0) : Line(x:10) mm. Draw () //Drawable (x:10) mm. X: Drawable (x:10) mmCopy the code

As you can see from the above code, mm can represent both Point and Line types. In fact, the actual type of MM is a special data type generated by the compilerExistential Container.Existential ContainerEncapsulate specific types to achieve storage consistencyExistential ContainerThe type occupies five memory units (also known asword, Word). Its structure is shown in the figure below:

  • Three words as a Value Buffer
  • One word is used as the index of the Value Witness Table
  • One word is used as the index of the Protocol Witness Table

The Value Buffer occupies three words and may store values or Pointers. For Small values (the storage space is less than or equal to the Value Buffer), they can be directly stored in the Value Buffer. For Large values (larger than or equal to the Value Buffer), memory space is allocated in the heap for storage, and the Value Buffer only stores its corresponding pointer

VWT(Value Witness Table)Is the lifecycle management of protocol types, which handles initialization, copying, destruction, etc

Protocol Witness Table (PWT) Method invocation of management protocols

Methods declared in Protocol are stored in PWT at the bottom. Methods in PWT are also found in the v-table of the class via class_method. If there are no functions declared in Protocol but a default implementation provided through Extension, whose function addresses are already determined during compilation, this method cannot be overridden for protocol-compliant classes

Storage of generic protocols

  • For class/struct/enum it is usedType parametersTo implement generics
  • For protocal, it adoptsAbstract type member Abstract Type MemberThe specific technology is calledAssociation types Associated Type

Next we will look at the storage of generic protocolsUsing the non-generic protocol example, we think the above code is ok because it hasExistential ContainerType ensures storage consistency. Because there areExistential ContainerType ensures storage consistency. But we ignore that the nature of generic protocols is to constrain types, not declare them, so the compiler will report an error. And we can verify it again with the following code

let x = gen.generate()
Copy the code

What is the type of x? The x type can be either Int or String. Swift itself is a type enforcement language. All types must be determined at compile time, so Swift cannot directly support the storage of generic protocols

Type erasure

In SWIFT, protocols can have association typesAssociated TypeFor generic-like functionality, but the protocol associated with the type is not a real type, so it cannot be used as a separate type declaration. The above discussion has been carried out, and the screenshot is attached:

How to deal with

As for the specific type encapsulated by generic protocol, this is actually a common solution in the industry. Many system libraries in SWIFT adopt this approach to solve the problem. The specific solution is as follows:

  • Defines a mid-tier structure that implements all methods of the protocol
  • In the concrete protocol method implemented by the mid-tier architecture, it is forwarded to the abstract type that implements the protocol
  • During initialization of the mid-tier architecture, the abstract type of the implementation protocol is passed in as a parameter (dependency injection)