🔰type erasure
╱🚧 under construction
Last updated
Was this helpful?
╱🚧 under construction
Last updated
Was this helpful?
Was this helpful?
🚧
📦 :type-erased shape value.
💡 :達到 type erasure 的效果。
import SwiftUI
/// protocol with Self requirement
public protocol Filter:
Equatable, // with `Self` requirement
Identifiable
{
var name: String { get }
func apply(to image: Image) -> Image
}
// -------------------------------------------------
// Type Erasure: turn PSR into nongeneric type
// -------------------------------------------------
// • PSR: protocol with `Self` requirement
// 1. define a nongeneric type conforming to protocol
public struct AnyFilter: Filter {
// 2. create members with matching signatures.
// (for each member in the protocol)
public let name: String
private let _apply: (Image) -> Image
// 3. erase the type of injected instance (⭐️)
// by storing a reference to all of the methods WITHOUT
// storing a reference to the injected instance (⭐️).
// ╭── ⭐️ ───╮
public init<F: Filter>(_ filter: F) {
name = filter.name
_apply = filter.apply(to:)
}
}
extension AnyFilter {
// `Filter` protocol conformance
// 4. forward all protocol method calls to stored references
public func apply(to image: Image) -> Image {
return _apply(image)
}
// `Equatable`
public static func == (lhs: AnyFilter, rhs: AnyFilter) -> Bool {
lhs.id == rhs.id
}
// `Identifiable`
public var id: String { "AnyFilter(\(name))" }
}
// --------------------------------------------------
// ⭐️ 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)
}
}