📅  最后修改于: 2023-12-03 15:20:25.318000             🧑  作者: Mango
Swift是一种类型安全的语言,这意味着编译器可以帮助我们在编译时捕捉到许多类型错误。但是对于我们编写的某些代码,我们可能不确定将要处理的值的确切类型。这正是泛型的用处所在。
泛型就是让代码可以处理任何类型的数据,而不需要重复编写多个相同的函数或类。泛型代码可以大大提高代码的可重用性、性能和灵活性。
在Swift中,我们可以使用泛型函数来处理任意类型的数据。泛型函数使用占位符类型来代替实际类型。这些占位符类型可以被用在函数体内作为参数类型、返回类型或者函数体内的临时变量。下面是一个例子:
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
在这个例子中,我们使用了<T>
来定义泛型占位符。在函数参数列表中,_ a: inout T
和 _ b: inout T
都表示这两个参数应该是同一个未知类型T。函数体中的临时变量temporaryA
是类型为T的值。函数用来交换a和b的值。
我们可以使用不同类型的变量调用这个函数,比如Int和String:
var a = 10
var b = 20
swapTwoValues(&a, &b)
print("a is now \(a), and b is now \(b)") // output: a is now 20, and b is now 10
var str1 = "hello"
var str2 = "world"
swapTwoValues(&str1, &str2)
print("str1 is now \(str1), and str2 is now \(str2)") // output: str1 is now world, and str2 is now hello
除了泛型函数,我们还可以定义泛型类型。泛型类型可以是类、结构体或枚举。下面是一个泛型Stack类的例子:
struct Stack<T> {
var items = [T]() // 泛型类型T的数组
mutating func push(_ item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
func peek() -> T {
return items[items.count - 1]
}
var isEmpty: Bool {
return items.isEmpty
}
}
Stack结构体有一个泛型类型T,用于存储items数组中的元素类型。结构体还有一个push方法,用于将元素添加到栈顶,并有一个pop方法用于从栈顶弹出一个元素。peek方法返回栈顶元素,而isEmpty属性用于检查栈是否为空。
我们可以将不同类型的值添加到Stack中:
var myStack = Stack<Int>()
myStack.push(10)
myStack.push(20)
myStack.push(30)
print(myStack.peek()) // output: 30
var anotherStack = Stack<String>()
anotherStack.push("hello")
anotherStack.push("world")
print(anotherStack.peek()) // output: world
有时候我们需要在协议中声明一个或多个类型,但是我们并不知道具体的类型是什么。这时候就可以用关联类型来解决这个问题。
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
在Container协议中,我们定义了一个关联类型ItemType。该类型在协议中使用,但是我们不知道ItemType的具体类型。实现该协议的类型需要提供ItemType的实际类型。
下面是一个实现了Container协议的IntStack结构体:
struct IntStack: Container {
// IntStack的原始实现部分
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// Container协议的实现部分
typealias ItemType = Int // 实际上,这是默认的,你可以直接忽略这个代码
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
这个结构体实现了Int类型的Stack,并通过遵循Container协议来进行扩展。我们还重新定义了Container协议的关联类型ItemType,这里使用了默认类型Int。同样,typealias ItemType = Int可以直接省略。由于遵循Container协议,我们需要提供必要的方法,如append、count和下标语法。
在Swift中,我们也可以对泛型类型进行扩展。当我们需要对已有类型进行更多泛型功能扩展时,这个功能就非常实用。我们可以使用泛型约束来限制扩展只能用于特定类型。下面是一个扩展Array的示例,它提供了排序方法:
extension Array where Element: Comparable {
func sortBySelection() -> [Element] {
var result = self
for i in 0 ..< result.count - 1 {
var minIndex = i
for j in i + 1 ..< result.count {
if result[j] < result[minIndex] {
minIndex = j
}
}
if i != minIndex {
result.swapAt(i, minIndex)
}
}
return result
}
}
在这个扩展中,我们使用Element这个泛型类型代表了数组中的元素类型。使用where Element: Comparable的语法来限制扩展只适用于实现了Comparable协议的元素类型。
泛型是Swift中一个非常强大的特性,它可以让我们更加灵活地编写通用代码,并提高代码的重用性。泛型函数、泛型类型、关联类型和泛型扩展都是泛型在Swift中的常见用法。如果你想编写优美的、易于扩展和重用的代码,泛型是不容错过的。