Reading this article requires a basic understanding of Swift's basic syntax and generics. This article contains about 840 words and takes about 10 minutes to read. Takeaway: More in-depth use of generics in Swift.Copy the code

Use generics instead of Any whenever possible

Suppose, in a project, we need to implement a Stack data structure and want to support any primitive type. So, we can use Any to implement the following code:

struct Stack {
    var data = [Any]()
    
    init(data: [Any]) {
        self.data = data
    }
    
    mutating func pop() {
        data.removeLast()
    }
    
    mutating func push(element: Any) {
        data.append(element)
    }
}

var stack = Stack(data: ["swift"."objective-c"])
stack.push(element: "iOS")
Copy the code

The code above meets our requirements, and it can support any type. However, there are a number of disadvantages in using this method:

  • Elements need to be unpacked and typed
print(stack.data.first as! String)
Copy the code
  • Because it is Any, it can be assigned to Any type, resulting in less readable and robust code
stack.push(element: 13)
//Error - Could not cast value of type 'Swift.Int' (0x10b4d7070) to 'Swift.String' (0x10b4d9190)
print(stack.data.last as! String) 
Copy the code

Therefore, we should use generics instead of Any when we encounter such requirements.

struct Stack<Element> {
    var data = [Element]()
    
    init(data: [Element]) {
        self.data = data
    }
    
    mutating func pop() {
        data.removeLast()
    }
    
    mutating func push(element: Element) {
        data.append(element)
    }
}

var stack = Stack(data: ["swift"."objective-c"])
stack.push(element: "iOS")
Copy the code
  • Just unpack without type conversion
print(stack.data.first!)
Copy the code
  • When used, the type is already determined. Assigning other types directly reports an error, thus improving the robustness of the code
// Compiling failed - Cannot convert value oftype 'Int' to expected argument type 'String'
stack.push(element: 13)
Copy the code

Implement common algorithms through generics and Extension

Following the example above, if we wanted to add a Stack to calculate the sum of all numeric elements in the Stack, we could do this with the following code:

extension Stack where Element: Numeric {
    func sum() -> Element {
        var total: Element = 0
        
        for num in data {
            total += num
        }
        returntotal } } var numStack = Stack(data: [1, 2]) numstack.sum () // 3 // strStack = Stack(data: [)"Swift"."iOS13"])
Copy the code

The sum() function can only be called if the elements in the Stack adhere to the Numberic protocol. This is done because non-numeric types cannot accumulate. Binary operator ‘+=’ cannot be applied to two ‘Element’ operands if we do not add this restriction.

Extend methods for Protocol through generics and Extension

Map (System Standard Library)

We can use generics + Extension to extend Protocol with multiple types of methods. For example, the map function provided by the standard library is implemented with generics + Extension:

extension Collection {
    func map<T>(_ transform: (Element) -> T) -> [T] {
        var result = [T]()
        
        result.reserveCapacity(self.count)
        var position = startIndex
        whileposition ! = endIndex { result.append(transform(self[position])) position = index(after: position) }return result
    }
}
Copy the code

Since we can determine the size of the result when we call the map() function, we can use result.reservecapacity (self.count) to optimize code efficiency. For more information about reserveCapacity, see Array Performance Comparison in Swift: Append vs reserveCapacity.

Shuffle (System Standard Library)

The Fisher-Yates shuffle algorithm is an algorithm used to generate a random array from a finite set.

extension RandomAccessCollection where Self: MutableCollection {
    mutating func shuffle() {
        let n = count
        guard n > 1 else { return }
        
        for (i, pos) in indices.dropLast().enumerated() {
            let otherPos = index(startIndex, offsetBy: Int.random(in: i.. < n)) swapAt (pos, otherPos)}}} var arr =,2,3,4,5,6,7 [1] arr. Shuffle () / / [4, 3, 5, 1, 6, 7, 2)print(arr)
Copy the code

reference

  • WWDC 2018