一、协议(Protocol)的核心内容
-
协议的基础
-
定义协议:声明方法、属性、下标或初始化器的蓝图。
protocol Drawable { func draw() var area: Double { get } }
-
协议继承与组合:
- 继承:
protocol Printable: Drawable { func print() }
- 组合:
typealias ClickablePrintable = Printable & Clickable
- 继承:
-
协议扩展:为协议提供默认实现。
extension Drawable { func draw() { print("Default drawing") } }
-
可选协议要求:使用
@objc
标记,仅类可遵循。@objc protocol DataSource { @objc optional func numberOfRows() -> Int }
-
定义协议:声明方法、属性、下标或初始化器的蓝图。
-
高级协议特性
-
关联类型(Associated Types):协议中的占位类型,由遵循协议的类型指定。
protocol Container { associatedtype Element mutating func append(_ item: Element) var count: Int { get } }
-
Self 关键字:表示遵循协议的具体类型。
protocol Equatable { static func == (lhs: Self, rhs: Self) -> Bool }
-
关联类型(Associated Types):协议中的占位类型,由遵循协议的类型指定。
-
协议作为类型
- 变量、参数或返回值可声明为协议类型。
func render(_ item: Drawable) { item.draw() }
- 变量、参数或返回值可声明为协议类型。
二、泛型(Generics)的核心内容
-
泛型基础
-
泛型函数:类型参数用
<T>
声明。func swap<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp }
-
泛型类型:如自定义集合。
struct Stack<Element> { var items: [Element] = [] mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element { items.removeLast() } }
-
泛型函数:类型参数用
-
类型约束
- 限制泛型类型必须遵循协议或继承类。
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? { for (index, item) in array.enumerated() { if item == value { return index } } return nil }
- 限制泛型类型必须遵循协议或继承类。
-
泛型 Where 子句
- 对关联类型或泛型参数进一步约束。
func allEqual<T: Sequence>(_ sequence: T) -> Bool where T.Element: Equatable { // 检查所有元素是否相等 }
- 对关联类型或泛型参数进一步约束。
-
条件遵循(Conditional Conformance)
- 泛型类型在特定条件下遵循协议。
extension Array: Equatable where Element: Equatable { // 自动实现数组的判等逻辑 }
- 泛型类型在特定条件下遵循协议。
三、协议与泛型的结合
-
协议中的泛型:关联类型
- 协议通过关联类型定义泛型行为。
protocol Queue { associatedtype Element mutating func enqueue(_ element: Element) mutating func dequeue() -> Element? } struct IntQueue: Queue { typealias Element = Int // 可省略,编译器自动推断 // 实现方法... }
- 协议通过关联类型定义泛型行为。
-
类型擦除(Type Erasure)
- 处理带有关联类型的协议,使其更易用。
struct AnyQueue<Element>: Queue { private let _enqueue: (Element) -> Void private let _dequeue: () -> Element? init<Q: Queue>(_ queue: Q) where Q.Element == Element { _enqueue = queue.enqueue _dequeue = queue.dequeue } // 实现方法... }
- 处理带有关联类型的协议,使其更易用。
-
不透明类型(Opaque Types)
- 使用
some
关键字返回协议实例,隐藏具体类型。func makeQueue() -> some Queue { return IntQueue() }
- 使用
四、需要掌握的内容总结
类别 | 核心知识点 |
---|---|
协议 | 协议定义、继承、扩展、可选方法、关联类型、Self、协议作为类型 |
泛型 | 泛型函数/类型、类型约束、where子句、条件遵循、泛型与协议结合 |
高级应用 | 类型擦除、不透明类型、协议与泛型在集合/算法中的实际应用 |
五、面试常见问题与回答思路
-
协议与类有什么区别?
- 回答:协议定义行为契约,无具体实现;类可继承、有状态。协议支持值类型和类,类仅支持引用类型。
-
关联类型的作用是什么?
- 回答:允许协议声明占位类型,由遵循协议的具体类型指定,实现协议级别的泛型。
-
如何解决协议中关联类型的类型擦除问题?
-
回答:通过类型擦除包装(如
AnySequence
)或使用不透明类型(some Protocol
)。
-
回答:通过类型擦除包装(如
-
泛型 Where 子句的应用场景?
-
回答:约束泛型参数或关联类型需满足额外条件,例如要求集合元素遵循
Equatable
。
-
回答:约束泛型参数或关联类型需满足额外条件,例如要求集合元素遵循
-
设计一个支持多种数据类型的缓存系统,如何结合协议与泛型?
-
回答:
protocol Cacheable { associatedtype Key: Hashable associatedtype Value func get(_ key: Key) -> Value? func set(_ key: Key, value: Value) } struct MemoryCache<K: Hashable, V>: Cacheable { private var storage: [K: V] = [:] func get(_ key: K) -> V? { storage[key] } func set(_ key: K, value: V) { storage[key] = value } }
-
回答:
六、实际应用场景
-
网络层:使用协议定义请求,泛型解析响应。
protocol APIRequest { associatedtype Response: Decodable var url: URL { get } } func fetch<T: APIRequest>(_ request: T) async throws -> T.Response { let data = try await downloadData(from: request.url) return try JSONDecoder().decode(T.Response.self, from: data) }
-
UI组件:泛型表格视图控制器,支持多种数据类型。
class GenericTableViewController<T: CellDisplayable>: UITableViewController { var items: [T] = [] // 根据 T 的类型动态配置单元格 }
七、总结
- 协议:定义行为规范,支持多态与代码复用。
- 泛型:提升代码灵活性,避免类型重复。
- 结合使用:通过关联类型和类型约束,实现高度抽象的通用逻辑。