Swift 泛型

Swift 泛型(Generics)详解

Swift 的泛型(Generics)是类型安全的参数化编程机制,让你编写灵活且可复用的函数、方法、类、结构体、枚举,而无需为每种类型重复代码。


1. 为什么需要泛型?

// 没有泛型:重复代码
func swapInts(_ a: inout Int, _ b: inout Int) { ... }
func swapStrings(_ a: inout String, _ b: inout String) { ... }
func swapDoubles(_ a: inout Double, _ b: inout Double) { ... }

使用泛型:

func swap<T>(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}

2. 泛型函数

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

// 使用
let index = findIndex(of: "Swift", in: ["Java", "Swift", "Kotlin"])

T: Equatable 表示 T 必须实现 ==


3. 泛型类型(结构体、类、枚举)

泛型栈(Stack)

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

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

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

    var top: Element? {
        items.last
    }
}

// 使用
var intStack = Stack<Int>()
intStack.push(5)
intStack.push(10)

var stringStack = Stack<String>()
stringStack.push("Hello")

4. 泛型协议(Type Erasure & Associated Types)

问题:不能直接用协议作为类型(存在类型开销)

protocol Animal {
    func speak()
}

不能这样写(Swift 5.6 以前):

let animals: [Animal] = [...] // 错误:协议不能作为具体类型

解决:关联类型(Associated Type)

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

实现:

struct IntStack: Container {
    typealias Item = Int  // 可省略,编译器推断
    private var items: [Int] = []

    mutating func append(_ item: Int) { items.append(item) }
    var count: Int { items.count }
    subscript(i: Int) -> Int { items[i] }
}

5. where 子句:约束增强

func allItemsMatch<C1: Container, C2: Container>(
    _ c1: C1, _ c2: C2
) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {
    if c1.count != c2.count { return false }
    for i in 0..<c1.count {
        if c1[i] != c2[i] { return false }
    }
    return true
}

6. 泛型扩展(Conditional Extensions)

extension Stack where Element: Equatable {
    func contains(_ item: Element) -> Bool {
        items.contains(item)
    }
}

// 仅当 Element 是 Equatable 时才有 contains

7. anysome 关键字(Swift 5.7+)

关键字含义
some Protocol不透明类型:某个具体类型,但隐藏具体类型
any Protocol存在类型:任意符合协议的类型(类型擦除)

some 示例(返回值)

func makeStack() -> some Container {
    return Stack<Int>()  // 编译器知道是 Stack<Int>,但调用者只知道是 Container
}

调用者不能访问 Stack 特有方法,但类型安全。

any 示例(存储)

var containers: [any Container] = [
    Stack<Int>(),
    [1, 2, 3] as Array<Int>
]

运行时类型擦除,性能略低,适合异构集合。


8. 类型擦除(Type Erasure)

当你想用协议作为类型但避免 any 性能开销:

final class AnyBox<T>: Box {
    private let _value: () -> T
    init<U: Box>(_ base: U) where U.Value == T {
        _value = { base.value }
    }
    var value: T { _value() }
}

高级用法,通常用 any 替代。


9. 泛型与协议组合(POP 核心)

protocol Repository {
    associatedtype Model: Codable
    func save(_ model: Model)
    func load() -> [Model]
}

struct UserRepository: Repository {
    func save(_ model: User) { ... }
    func load() -> [User] { ... }
}

10. 常用泛型系统类型

类型说明
Optional<T>T?
Array<Element>[Element]
Dictionary<Key: Hashable, Value>[Key: Value]
Result<Success, Failure>异步结果封装
AnyPublisher<Output, Failure>Combine 框架

11. 泛型实战:JSON 解析器

struct JSONDecoder<T: Decodable> {
    func decode(from data: Data) throws -> T {
        return try JSONDecoder().decode(T.self, from: data)
    }
}

// 使用
let user: User = try JSONDecoder<User>().decode(from: data)

12. 性能提示

场景建议
频繁使用 any Protocol可能有动态派发开销
泛型函数编译时专特化,零开销
where 约束优先于 Any

推荐实践(Best Practices)

// 1. 优先泛型而非 Any
func process<T>(_ items: [T]) { ... }

// 2. 小而专注的协议 + 泛型
protocol Fetchable { associatedtype ID; func fetch(id: ID) -> Self? }

// 3. 使用 where 提高可读性
extension Array where Element: Hashable { ... }

// 4. 避免过度泛型
// 坏:func log<T>(_ value: T) { print(value) }
// 好:func log(_ value: CustomStringConvertible)

泛型 vs 协议 vs 继承

特性泛型协议继承
类型安全
代码复用
运行时开销中(any)
灵活性

Swift 推荐:面向协议编程 + 泛型(POP + Generics)


面试高频题

  1. 泛型函数如何约束多个类型相同?
   where T == U
  1. some vs any 的区别?
  • some: 隐藏具体类型,编译时确定
  • any: 类型擦除,运行时动态
  1. 如何让协议有默认实现?
   extension Protocol { func method() { ... } }

需要 泛型 + 协议实战项目?如:

  • 通用网络层(APIClient<T: Decodable>
  • 依赖注入容器(Resolver<T>
  • 状态管理(Store<State, Action>

欢迎继续提问!

文章已创建 2588

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部