🔰type erasure
╱🚧 under construction
🚧
📦 AnyShape:type-erased shape value.
💡 用 enum 封裝不同型別:達到 type erasure 的效果。
💈範例
// --------------------------------------------------
// ⭐️ generic protocol (with associated type)
// --------------------------------------------------
protocol Fetcher {
associatedtype Data
typealias ResultHandler = (Result<Data, Error>) -> Void
func fetch(completion: ResultHandler)
}
// conforming type
struct User {
let id: Int
let name: String
}
// protocol conformance
struct UserFetcher: Fetcher {
typealias Data = User
func fetch(completion: ResultHandler) {
let user = User(id: 1, name: "Phil")
completion(.success(user))
}
}
/*
⭐️ The Problem
--------------
How do we hold a reference to an object that has
implemented this protocol?
*/
//struct SomeStruct {
// let fetcher: Fetcher
//}
// ⛔ Protocol ‘Fetcher’ can only be used as a generic constraint
// because it has Self or associated type requirements
// 💬 The compiler CANNOT determine the associated type `Data`
// from the declaration.
// ------------------------------------------------
// Type Erasure: turn PAT into generic type
// ------------------------------------------------
// 1. define a generic type conforming to protocol
struct AnyFetcher<T>: Fetcher {
// 2. turn associated types into type parameters.
typealias Data = T
// 3. create members with matching signatures.
// (for each member in the protocol)
private let _fetch: (ResultHandler) -> Void
// 4. erase the type of injected instance (⭐️)
// by storing a reference to all of the methods WITHOUT
// storing a reference to the injected instance (⭐️).
// ╭─── ⭐️ ───╮
init<F: Fetcher>(_ fetcher: F) where F.Data == T {
// store reference to methods, not the object.
_fetch = fetcher.fetch
}
// 5. forward all protocol method calls to stored references
func fetch(completion: ResultHandler) {
_fetch(completion)
}
}
// ----------------
// use case
// ----------------
// turn `UserFetcher` into `AnyFetcher` (erase `UserFetch` type)
let fetcher = AnyFetcher(UserFetcher())
// let it do fetch
fetcher.fetch { result in
switch result {
case .success(let user) : print(user.name)
case .failure(let error): print(error)
}
}
Last updated
Was this helpful?