Swift如何使用类型擦除及自定义详解

2020-01-09 00:16:13王冬梅


func f<S: Sequence>(seq: S) where S.Element == Int { ...

有时候这样写完全可以,但有些地方还存在一些比较麻烦的情况,通常你不可能只在一个地方添加泛型: 一个泛型函数对其他泛型要求更多… 更糟糕的是,你不能将泛型作为返回值或者属性。这就跟我们想的有点不一样了。


func g<S: Sequence>() -> S where S.Element == Int { ...

我们希望函数 g 能返回任何符合的类型,但上面这个不同,它允许调用者选择他所需要的类型,然后函数 g 来提供一个合适的值。

Swift 标准库中提供了 AnySequence 来帮助我们解决这个问题。AnySequence 包装了一个任意类型的序列,并擦除了它的类型。使用 AnySequence 来访问这个序列,我们来重写一下函数 f 与 函数 g:


func f(seq: AnySequence<Int>) { ...

func g() -> AnySequence<Int> { ...

泛型部分不见了,同时具体的类型也被隐藏起来了。由于使用了 AnySequence 包装具体的值,它带来了一定的代码复杂性以及运行时间成本。但是代码却更简洁了。

Swift 标准库中提供了很多这样的类型,如 AnyCollection、AnyHashable 及 AnyIndex。这些类型在你自定义泛型或协议的时候非常的管用,你也可以直接使用这些类型来简化你的代码。接下来让我们探索实现类型擦除的多种方式吧。

基于类的类型擦除

有时我们需要在不暴露类型信息的情况下从多个类型中包装一些公共的功能,这听起来就像是父类-子类的关系。事实上我们的确可以使用抽象父类来实现类型擦除。父类提供 API 接口,不用去管谁来实现。而子类根据具体的类型信息实现相应的功能。

接下来我们将使用这种方式来自定义 AnySequence,我们将其命名为 MAnySequence: