简介
泛型是 Swift 最强大的特性之一,很多 Swift 标准库是基于泛型代码构建的。
泛型是使代码具备通用性和复用性的重要特性。泛型允许我们编写可以适用于多种类型的灵活函数和类型,而不必重复编写多个函数版本。
在没有泛型的情况下,我们常常需要为不同类型写重复的代码。
func swapInts(_ a: inout Int, _ b: inout Int) {
...
}
func swapStrings(_ a: inout String, _ b: inout String) {
...
}
泛型的核心是占位类型,即在定义时使用占位符表示数据类型,而具体类型由调用时决定。Swift 一般用尖括号 < > 包裹。
1.泛型函数
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
func makeArray<T>(repeating item: T, count: Int) -> [T] {
return Array(repeating: item, count: count)
}
这里 T 是泛型参数,占位符可以代表任意类型。在函数调用时,编译器会根据传入的参数类型推断出 T 的实际类型。
除了
T,也可以用其他任意代号,如Element,Base等
调用
self.swapValues(23, 44)
self.swapValues("Ksdme", "Plkdfm")
2.泛型类型
- 泛型结构体
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
调用
var intStack = Stack<Int>()
intStack.push(10)
- 泛型类型
class Box<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
- 泛型枚举
enum Result<Success, Failure> {
case success(Success)
case failure(Failure)
}
3.泛型协议
关联类型(Associated Types)
用在协议中定义一个“占位类型”。具体类型,由协议遵守者决定。
例
protocol Container {
associatedtype XO
mutating func append(_ item: XO)
var count: Int { get }
subscript(i: Int) -> XO { get }
}
遵守协议,具体实现,编译器会推断具体类型
struct IntStack: Container {
var items: [Int] = []
mutating func append(_ item: Int) {
items.append(item)
}
var count: Int { return items.count }
subscript(i: Int) -> Int {
return items[i]
}
}
另外,也可使用关键字associatedtype+typealias,决定具体类型
protocol Stackable {
associatedtype Element
mutating func push(_ element: Element)
mutating func pop() -> Element
}
class StringStack : Stackable {
typealias Element = String // 确定 关联类型为 String
var elements = [String]()
func push(_ element: String) {
elements.append(element)
}
func pop() -> String {
elements.removeLast()
}
}
泛型约束
默认泛型参数可以是任何类型,但你可以限制它必须符合某个协议。
- 使用
: XXX,规定该类型需遵从XXX协议
如
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, element) in array.enumerated() {
if element == value {
return index
}
}
return nil
}
- 使用
where语句:添加更复杂的约束条件
在函数中
func allItemsMatch<C1: Container, C2: Container>(_ a: C1, _ b: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
...
}
func fetch<T: Decodable>(url: URL, completion: @escaping (Result<T, Error>) -> Void) {
...
}
在协议
public struct KingfisherWrapper<Base>{
public let base: Base
public init(_ base: Base) {
self.base = base
}
}
extension KingfisherWrapper where Base: UIButton {
}
示例
我们写了一个工具方法,用于加载JSON文件并转化为 User实例
// 传入JSON数据文件名,最后转化为 实例
func decode(_ file: String) -> [String: User] {
// 查找文件路径
guard let url = Bundle.url(forResource: file, withExtension: nil) else {
fatalError()
}
// 根据路径把文件转为Data
guard let data = try? Data(contentsOf: url) else {
fatalError()
}
// 把JSON数据转化为 实例
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode([String: User].self, from: data) else {
fatalError()
}
return loaded
}
如果,我们需要处理除User之外的其他类型,这时就是泛型的好处了。
// 传入JSON数据文件名,最后转化为 实例
func decode<T: Codable>(_ file: String) -> T {
// 查找文件路径
guard let url = Bundle.url(forResource: file, withExtension: nil) else {
fatalError()
}
// 根据路径把文件转为Data
guard let data = try? Data(contentsOf: url) else {
fatalError()
}
// 把JSON数据转化为 实例
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError()
}
return loaded
}
注意,此例中T: Codable表示该泛型必须是 遵守 Codable协议的
此外,调用时需要 带上 类型注释,因为此时无法进行 类型推断
let user: [String: User] = Bundle.main.decode("user.json")