Introduction to Swift generics

Generics is a syntax for the flexibility of Swift programming. It is fully used in functions, enumerations, structures and classes. Its introduction can serve as a placeholder when the type is temporarily uncertain and the specific type can only be determined when the function is called.

We’ve actually used generics before, for example: Swift’s Array and Dictionary types are sets of generics.

You can create an Int array, you can create a String array, or even any other Swift data array. Similarly, you can create dictionaries that store any given type, and those types can be unlimited.

Why do we use generics? Here is a simple example to illustrate the benefits of using generics

// Define a function to append array data to a specified array
func appendIntToArray(src:[Int],inout dest:[Int])
{
	// iterate over and append to array
	for element in src{
		dest.append(element)
	}
}
// Use copyIntArray to add integer array data
var arr = [2.5]
appendIntToArray([12.9], dest: &arr)
print(arr) / /,5,12,9 [2]

// Add a string
func appendStringToArray(src:[String],inout dest:[String])
{
	for element in src{
		dest.append(element)
	}
}
// Use copyStringArray to add string array data
var strArr = ["oc"."swift"]
appendStringToArray(["php"."C#"], dest: &strArr)
print(strArr) // ["oc", "swift", "php", "C#"]
// What if you need to implement additional types?
// Write a corresponding function for each type, which would be too complicated. This is where generics come in
// Define a generic function by adding 
      
        to the name of the normal function. T is a type holder and can represent any type
      
func appendArray<T>(src:[T],inout dest:[T])
{
	for element in src
	{
		dest.append(element)
	}
}
// See how powerful it is? And then feel free to use it
var arr2 = [5.8]
appendArray([9.58], dest: &arr2) AppendArray automatically identifies the array data type to be added
print(arr2) / / [5, 8, 9, 58]
var strArr2 = ["renhairui"."hello"]
appendArray(["nihao"."helloworld"], dest: &strArr2)
print(strArr2) //["renhairui", "hello", "nihao", "helloworld"]
var doubleArr = [1.2.3.4]
appendArray([6.5.1.0], dest: &doubleArr)
print(doubleArr) / / [1.2, 3.4, 6.5, 1.0]
Copy the code

Generics is to occupy the pit first, occupy the pit to do what, up to you

Swift generics

Related uses of Swift generics fall into the following categories:

  1. Generic function
  2. The generic type
  3. Generic constraint
  4. The generic protocol
1. Generic functions. Function parameters or return value types are represented by generics
// The generic function definition
funcFunction name < generic 1, generic 2... >(Parameter list)-> Return value type {// Function body...
}
Copy the code
Examples of generic function usage
// Define a generic function that swaps the values of the two arguments
func swapTwoValues<T>(inout valueOne: T, inout valueTwo: T) {
	let temporaryA = valueOne
	valueOne = valueTwo
	valueTwo = temporaryA
}
// Swap two integer variables
var oneInt = 3
var twoInt = 107
swapTwoValues(&oneInt, valueTwo: &twoInt)
print("oneInt = \(oneInt), twoInt = \(twoInt)")
// Print: oneInt = 107, twoInt = 3
// Exchange two string variables
var oneStr = "hello"
var twoStr = "world"
swapTwoValues(&oneStr, valueTwo: &twoStr)
print("oneStr = \(oneStr), twoStr = \(twoStr)")
// Print: oneStr = world, twoStr = hello
Copy the code
2. Generic types, which are used when defining types

It is used in much the same way as a generic function, with the type name followed by < generic 1, generic 2… >, and then use generics directly within the type

// Define a generic structure for pushing and pushing. Generic types can use classes, structures, enumerations, etc
struct Stack<T> {
	// Stack is an array storage. The data types stored in arrays are generic types
    var items = [T] ()// The mutationg keyword needs to be added because pressing will change the instance value
    mutating func push(item: T) {
        items.append(item)
    }
	// The mutationg keyword is needed because the instance value will be changed when the stack is removed
    mutating func pop(a) -> T {
        return items.removeLast()
    }
}
// Create a string stack containing strings
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
print("Out of the stack:\(stackOfStrings.pop()), remaining in stack:\(stackOfStrings.items)")
print("Out of the stack:\(stackOfStrings.pop()), remaining in stack:\(stackOfStrings.items)")
print("Out of the stack:\(stackOfStrings.pop()), remaining in stack:\(stackOfStrings.items)")
["uno", "DOS ", "tres"] ["uno"," DOS "] */
// create an integer stack
var stackOfInt = Stack<Int>()
stackOfInt.push(12)
stackOfInt.push(32)
stackOfInt.push(45)
stackOfInt.push(35)
print("Out of the stack:\(stackOfInt.pop()), remaining in stack:\(stackOfInt.items)")
print("Out of the stack:\(stackOfInt.pop()), remaining in stack:\(stackOfInt.items)")
print("Out of the stack:\(stackOfInt.pop()), remaining in stack:\(stackOfInt.items)")
/* Print: [12, 32, 45] : [12, 32] : [12] */
Copy the code

3. Generic constraints, add constraints for generic types

Generic constraints fall into the following categories:
  1. Inheritance constraints, the generic type must be a subclass type of a class
  2. Protocol constraints, generic types must follow certain protocols
  3. Conditional, a generic type must satisfy some condition
The approximate format used for the constraint
// Inheritance constraints use format
funcFunction name < generic: Inherits parent class >(Parameter list)-> return value {// The body of a function. A generic type is a subclass of a class
}
// Protocol constraints use format
funcFunction name < generic: Protocol >(Parameter list)-> return value {// The body of a function. Generic types follow certain protocols
}
// Constraints use format
funcFunction name < generic 1, generic 2 where condition >(Parameter list)-> return value {// The body of the function, the generic type satisfies some conditions
}
Copy the code
Example use of inheritance constraints
// Define a parent class, animal class
class Animal{
	// Animals can run
	func run(a){
		print("Animal run")}}// Define dog class, inherit animal class
class Dog: Animal {
	override func run(a){// Override the parent method
		print("Dog run")}}// Define the cat class, inherit the animal class
class Cat: Animal {
	override func run(a){// Override the parent method
		print("Cat run")}}// Define a generic function that takes a generic parameter and requires that the generic type must inherit from Animal
func AnimalRunPint<T:Animal>(animal:T){
	animal.run() // All subclasses that inherit from Animal have run methods to call
}
AnimalRunPint(Dog())
AnimalRunPint(Cat())
/* Prints: Dog run Cat run */
Copy the code
Example usage of protocol constraints

The Swift standard library defines an Equatable protocol that requires any type that follows to implement equality (==) and inequality (! =) compare any two of these types. All Swift standard types automatically support the Equatable protocol.

// Define generic functions to add protocol constraints to generics. Generic types must comply with the Equatable protocol
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
	var index = 0
    for value in array {
        if value == valueToFind {// Since the Equatable protocol is followed, an equality comparison can be made
            return index
        } else {
			index++
		}
    }
    return nil
}
Double follows the Equatable protocol by default
let doubleIndex = findIndex([3.14159.0.1.0.25], valueToFind: 9.3)
if let index = doubleIndex {
	print("Find 9.3 in floating-point array, find index as\(index)")}else {
	print("9.3 not found in floating point array")}String follows the Equatable protocol by default
let stringIndex = findIndex(["Mike"."Malcolm"."Andrea"], valueToFind: "Andrea")
if let index = stringIndex {
	print("Andrea is found in the array of strings\(index)")}else {
	print("Andrea not found in string array")}/* Print: Andrea not found in float array 9.3 Andrea found in string array with index 2 */
Copy the code

4. Generic agreements and conditional constraints

The Equatable protocol above is actually not a generic protocol, but a generic protocol, and assuming that generic types must follow a protocol, an association type must be introduced into the protocol to solve this problem.

// Define a generic protocol, unlike other generics, where generics are used in the form of associated types
protocol Stackable{
    // Declare an association type using the typeAlias keyword
    typealias ItemType
    mutating func push(item:ItemType)
    mutating func pop(a) -> ItemType
}
 
struct Stack<T> :Stackable{
    var store = [T] ()mutating func push(item:T){// Implement the protocol's push method requirements
        store.append(item)
    }
    mutating func pop(a) -> T {// Implement the protocol's POP method requirements
        return store.removeLast()
    }
}
// Create a Stack structure with a generic type String
var stackOne = Stack<String>()
stackOne.push("hello")
stackOne.push("swift")
stackOne.push("world")
let t = stackOne.pop()
print("t = \(t)") // Result: t = world

C1 and C2 must follow the Stackable protocol, and C1 and C2 must contain the same generic types
func pushItemOneToTwo<C1: Stackable, C2: Stackable 
		where C1.ItemType == C2.ItemType>(inout stackOne: C1, inout stackTwo: C2) 
{The ItemType attribute can be called because C1 and C2 follow the Stackable protocol
	let item = stackOne.pop()
	stackTwo.push(item)
}
// Define another structure type that implements the same protocol as Stack
struct StackOther<T> :Stackable{
    var store = [T] ()mutating func push(item:T){// Implement the protocol's push method requirements
        store.append(item)
    }
    mutating func pop(a) -> T {// Implement the protocol's POP method requirements
        return store.removeLast()
    }
}
// Create StackOther with a generic type of String
var stackTwo = StackOther<String>()
stackTwo.push("where")
// Although stackOne and stackTwo are different, generic types follow the Stackable protocol as well
pushItemOneToTwo(&stackOne, stackTwo: &stackTwo )
print("stackOne = \(stackOne.store), stackTwo = \(stackTwo.store)")
// Print: stackOne = ["hello"], stackTwo = ["where", "swift"]
Copy the code

Feel free to ask any questions in the comments section below! O (studying studying) O ha!