Swift类型擦除:该场景下如何实现?
解决Swift带关联类型协议的类型擦除问题
哈哈,这个问题我太熟了——带关联类型的TypeConverter协议确实没法直接用来声明变量或者作为参数类型,Swift的类型系统会因为没法确定关联类型的具体类型而报错。不过咱们用**类型擦除(Type Erasure)**就能完美搞定这个问题,本质就是用一个泛型包装类把具体的转换器类型给“藏”起来,对外只暴露明确的输入输出类型。
第一步:实现类型擦除的包装类
我们先定义一个AnyTypeConverter<A, B>类,它本身符合TypeConverter协议,内部持有一个任意符合TypeConverter的实例,把协议方法的调用转发给这个实例:
protocol TypeConverter { associatedtype A associatedtype B func transformFrom(fromType: A?) -> B? func transformTo(toType: B?) -> A? } // 类型擦除的核心包装类 class AnyTypeConverter<A, B>: TypeConverter { // 用闭包捕获具体转换器的方法 private let _transformFrom: (A?) -> B? private let _transformTo: (B?) -> A? // 初始化方法,接收任意符合TypeConverter的实例,约束其关联类型和当前泛型参数一致 init<Converter: TypeConverter>(_ converter: Converter) where Converter.A == A, Converter.B == B { self._transformFrom = converter.transformFrom self._transformTo = converter.transformTo } // 实现协议方法,转发给内部的闭包 func transformFrom(fromType: A?) -> B? { return _transformFrom(fromType) } func transformTo(toType: B?) -> A? { return _transformTo(toType) } }
第二步:使用包装类声明变量
现在你就可以用AnyTypeConverter<A, B>来声明变量了,把你的具体转换器实例包装进去就行。比如你的IntToStringTypeConverter:
class IntToStringTypeConverter: TypeConverter { typealias A = Int typealias B = String func transformFrom(fromType: Int?) -> String? { guard let fromType = fromType else { return nil } return String(fromType) } func transformTo(toType: String?) -> Int? { guard let toType = toType else { return nil } return Int(toType) } } // 现在可以正常声明变量了! let converter: AnyTypeConverter<Int, String> = AnyTypeConverter(IntToStringTypeConverter()) // 调用方法完全正常 let numberAsString = converter.transformFrom(fromType: 42) // 结果是"42" let stringAsNumber = converter.transformTo(toType: "123") // 结果是123
第三步:优化使用体验(可选)
为了让代码更简洁,我们可以给TypeConverter协议加一个扩展,提供一个快速生成AnyTypeConverter的方法:
extension TypeConverter { func erase() -> AnyTypeConverter<A, B> { return AnyTypeConverter(self) } } // 现在创建包装类的代码更清爽 let converter = IntToStringTypeConverter().erase()
原理说明
AnyTypeConverter的作用就是把具体的转换器类型(比如IntToStringTypeConverter)给“擦除”了,对外只暴露A和B这两个明确的泛型参数。这样Swift的类型系统就能确定变量的具体类型,不会再因为关联类型的不确定性而报错——所有的类型信息都通过泛型参数明确下来了,内部的闭包负责把方法调用转发给真正的转换器实例。
内容的提问来源于stack exchange,提问作者Leon Dobnik
相关产品推荐
相关产品推荐

