【发布时间】:2017-07-08 15:20:10
【问题描述】:
使用 Swift 3.0(如果对我有帮助的话,我可以使用 Swift 4.0……但我认为不会)我想键入 Erase 两个级别。我要键入什么擦除具有关联类型的协议,该协议符合本身具有关联类型的协议。所以有人可以说我想键入擦除 nested 关联类型。
下面的代码是我的代码的一个极其简化的版本,但这样更清楚。所以我真正想要的是这样的:
原始场景 - 未解决
protocol Motor {
var power: Int { get }
}
protocol Vehicle {
associatedType Engine: Motor
var engine: Engine { get }
}
protocol Transportation {
associatedType Transport: Vehicle
var transport: Transport { get }
}
然后我想输入erase Transportation 并能够存储AnyTransportation 的数组,该数组可以有任何Vehicle,而Motor 又可以有任何Motor。
所以这是一个包含 3 个协议的场景,其中 2 个具有 (nested) 关联类型。
我不知道该怎么做。实际上,我什至不知道如何解决更简单的情况:
简化方案 - 未解决
我们可以将上面的原始场景简化为我们有 2 个协议的版本,其中只有 1 个有关联类型:
protocol Vehicle {
var speed: Int { get }
}
protocol Transportation {
associatedtype Transport: Vehicle
var transport: Transport { get }
var name: String { get }
}
然后假设我们有一个符合Vehicle 的Bus:
struct Bus: Vehicle {
var speed: Int { return 60 }
}
然后我们有两个不同的BusLines,RedBusLine 和 BlueBusLine 都符合 Transportation
struct RedBusLine: Transportation {
let transport: Bus
var name = "Red line"
init(transport: Bus = Bus()) {
self.transport = transport
}
}
struct BlueBusLine: Transportation {
let transport: Bus
var name = "Blue line"
init(transport: Bus = Bus()) {
self.transport = transport
}
}
然后我们可以使用bignerdranch here 所描述的基本和盒子模式和类来输入erase Transportation:
final class AnyTransportation<_Transport: Vehicle>: Transportation {
typealias Transport = _Transport
private let box: _AnyTransportationBase<Transport>
init<Concrete: Transportation>(_ concrete: Concrete) where Concrete.Transport == Transport {
box = _AnyTransportationBox(concrete)
}
init(transport: Transport) { fatalError("Use type erasing init instead") }
var transport: Transport { return box.transport }
var name: String { return box.name }
}
final class _AnyTransportationBox<Concrete: Transportation>: _AnyTransportationBase<Concrete.Transport> {
private let concrete: Concrete
init(_ concrete: Concrete) { self.concrete = concrete; super.init() }
required init(transport: Transport) { fatalError("Use type erasing init instead") }
override var transport: Transport { return concrete.transport }
override var name: String {return concrete.name }
}
class _AnyTransportationBase<_Transport: Vehicle> : Transportation {
typealias Transport = _Transport
init() { if type(of: self) == _AnyTransportationBase.self { fatalError("Use Box class") } }
required init(transport: Transport) { fatalError("Use type erasing init instead") }
var transport: Transport { fatalError("abstract") }
var name: String { fatalError("abstract") }
}
然后我们可以将RedBusLine 或BlueBusLine 放入
let busRides: [AnyTransportation<Bus>] = [AnyTransportation(RedBusLine()), AnyTransportation(BlueBusLine())]
busRides.forEach { print($0.name) } // prints "Red line\nBlue line"
在上面链接的关于类型擦除的博客文章中,我想要的实际上是Homogeneous Requirement 的解决方法。
假设我们有另一个Vehicle,例如Ferry 和FerryLine:
struct Ferry: Vehicle {
var speed: Int { return 40 }
}
struct FerryLine: Transportation {
let transport: Ferry = Ferry()
var name = "Ferry line"
}
我想我们现在想输入erase Vehicle?因为我们想要一个AnyTransportation<AnyVehicle> 的数组,对吧?
final class AnyVehicle: Vehicle {
private let box: _AnyVehicleBase
init<Concrete: Vehicle>(_ concrete: Concrete) {
box = _AnyVehicleBox(concrete)
}
var speed: Int { return box.speed }
}
final class _AnyVehicleBox<Concrete: Vehicle>: _AnyVehicleBase {
private let concrete: Concrete
init(_ concrete: Concrete) { self.concrete = concrete; super.init() }
override var speed: Int { return concrete.speed }
}
class _AnyVehicleBase: Vehicle {
init() { if type(of: self) == _AnyVehicleBase.self { fatalError("Use Box class") } }
var speed: Int { fatalError("abstract") }
}
// THIS DOES NOT WORK
let rides: [AnyTransportation<AnyVehicle>] = [AnyTransportation(AnyVehicle(RedBusLine())), AnyTransportation(AnyVehicle(FerryLine()))] // COMPILE ERROR: error: argument type 'RedBusLine' does not conform to expected type 'Vehicle'
当然这不起作用...因为AnyTransportation 期望传入符合Transportation 的类型,但AnyVehicle 当然不符合它。
但我还没有找到解决方案。有吗?
问题 1:是否可以在 Simple Scenario 中键入擦除:[AnyTransportation<AnyVehicle>]?
问题2:如果简单场景是可解的,那么原来的场景也可以解吗?
下面仅对我想通过原始方案实现的目标进行更详细的说明
原始场景 - 扩展
我最初的需要是把任何Transportation,有任何Vehicle,本身有任何Motor放在同一个数组中:
let transportations: [AnyTransportation<AnyVehicle<AnyMotor>>] = [BusLine(), FerryLine()] // want to put `BusLine` and `FerryLine` in same array
【问题讨论】:
-
这篇文章可能会有所帮助:gist.github.com/dtartaglia/0b5188eaa825b1239389b377d8cb23c1 > Swift 3 中的两级类型擦除 > > 最近我将我的一个项目转换为 Swift 3 > (github.com/dtartaglia/XStreamSwift) 我不得不处理一个 >我找不到答案的问题。这篇文章是关于 > 问题和我发现的解决方案。
标签: ios swift swift3 type-erasure