Type Erasure

Type erasure,翻译成中文是类型擦除,通过抽象使程序使用某类型时不依赖它的类型信息。

其实,我们在Objective-C或者Swift中很早以前就用过这个技巧。比如利用基类(superclass)或者协议(protocol)来隐藏掉继承它们的子类的信息。

class Pet { }
class Dog: Pet { }
class Cat: Pet { }
// 1. 隐藏具体宠物类型信息
let pets: [Pet] = [Dog(), Cat()]

class WikipediaSearchViewModel {
    struct Input {
        let searchText: Observable<String>
    }
    struct Output {
        let searchDescription: Observable<String>
    }
    func transform(input: Input) -> Output {
        let _searchDescription = PublishRelay<String>()
        // 使用_searchDescription

        // 2. 对外只是Observable,隐藏掉PublishRelay的功能
        return Output(searchDescription:_searchDescription.asObservable())
    }
}

使用类型擦除常见的目的有两种:

  1. 把不同类型放在同一个数组或者变量中保存,然后统一处理。如上例中的1。
  2. 减少对外不必要的信息泄露 。如上例中的2。

今天特别讨论这个话题是因为Swift的一些特性,以下类型擦除的场景下,我们需要利用其他技巧。

// 1. protocol中引用Self当前类型
public protocol Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool
}
[Equatable] 🙅‍♂️

// 2. 类型中有associatedtype
protocol Request {
    associatedtype Response
    associatedtype Error: Swift.Error
    typealias Handler = (Result<Response, Error>) -> ()
    func send(completion: @escaping Handler)
}
[Request] 🙅‍♂️

// 3. 范型Generic
enum Either<T, U> {
    case left(T)
    case right(U)
}
[Either] 🙅‍♂️

// 包括特殊的范性Phantom type
struct Money<C: CurrencyType> {
    var amount: Decimal
}
[Money] 🙅‍♂️

对于这些情况,通常类型擦除有两种方法,利用Closure闭包或者Wrapper Object来封装。在文章末尾的参考资料2中,有对这两种方法的详细讲解。所以这里我只简单描述其中的例子,着重讲一下参考资料2中未提及的注意点。

方法1 利用Closure闭包

```swift
class RequestQueue {
private var queue: [() -> ()] = []
func add(
_ request: R,

top Created with Sketch.