Swift 中的泛型入门:为何需要以及如何使用

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程
```html

引言:为什么在 Swift 中离不开泛型?

想象一下,你需要编写一个函数来相加两个数字。然后——再写一个函数来拼接两个字符串。接着——还要处理数组。如果没有泛型编程(Generics),你就必须为每种数据类型创建单独的代码版本。这不仅会增加代码量,还会使维护变得更加复杂。

Swift 中的泛型(Generics)是一个强大的工具,它允许你编写灵活、可复用且类型安全的代码。它们是诸如 Array、Dictionary 和 Optional 等流行结构的基础。在本文中,我们将探讨为什么需要泛型,如何在实际中使用它们,以及为什么每个初学 iOS/macOS 的开发人员都应该掌握这个主题。

用简单的话来说,什么是泛型?

泛型(Generics)是一种编写代码的方式,它能够处理任何数据类型,同时又不牺牲 Swift 严格的类型检查。你不必指定具体的类型(例如 Int 或 String),而是使用一个占位符(通常是字母 T),在调用函数或创建结构时,这个占位符会被实际的类型所替换。

没有泛型时的问题

假设你要编写一个交换两个变量值的函数。没有泛型,你就得这样写:

func swapInts(_ a: inout Int, _ b: inout Int) {
    let temp = a
    a = b
    b = temp
}

func swapStrings(_ a: inout String, _ b: inout String) {
    let temp = a
    a = b
    b = temp
}

这显然是重复的代码。如果需要交换 Double 或自定义类型,函数的数量还会继续增加。

使用泛型的解决方案

一个通用的函数就能替代上面所有的函数:

func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

// 使用:
var x = 10
var y = 20
swapValues(&x, &y) // x = 20, y = 10

var firstName = "安娜"
var lastName = "伊万诺娃"
swapValues(&firstName, &lastName) // firstName = "伊万诺娃"

尖括号 <T> 声明了一个类型占位符。编译器会自动确定在第一种情况下 T 是 Int,在第二种情况下是 String。代码变得简洁且安全。

如何使用泛型:主要场景

Swift 中的泛型可以应用于函数、结构体、类、枚举和协议。让我们看看最常见的几种情况。

1. 泛型函数

我们已经看到了 swapValues 的例子。但我们可以更进一步——创建一个在数组中查找元素索引的函数:

func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
    for (index, element) in array.enumerated() {
        if element == value {
            return index
        }
    }
    return nil
}

let numbers = [1, 5, 9, 12]
if let index = findIndex(of: 9, in: numbers) {
    print("索引:\(index)") // 索引:2
}

请注意这里的 <T: Equatable>。这是一个类型约束(type constraint)。它的意思是:“T 必须遵循 Equatable 协议,这样我们才能使用 == 运算符”。如果没有这个约束,代码将无法编译。

2. 泛型类型(结构体和类)

让我们创建一个简单的“栈”(LIFO)结构,它可以存储任何类型:

struct Stack<Element> {
    private var items: [Element] = []

    mutating func push(_ item: Element) {
        items.append(item)
    }

    mutating func pop() -> Element? {
        return items.popLast()
    }

    func peek() -> Element? {
        return items.last
    }
}

// 使用:
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
print(intStack.pop
```                

博客

书籍推荐