Saj*_*jon 5 type-erasure ios swift swift3
使用Swift 3.0(我可以使用Swift 4.0,如果这对我有帮助......但我认为不会)我想要Erase两级.我要键入什么来擦除具有相关类型的协议,该协议符合协议本身又具有相关类型的协议.所以可以说我想键入擦除嵌套关联类型.
下面的代码是我的代码的极其简化的版本,但它更清楚.所以我真正想要的是这样的:
protocol Motor {
var power: Int { get }
}
protocol Vehicle {
associatedType Engine: Motor
var engine: Engine { get }
}
protocol Transportation {
associatedType Transport: Vehicle
var transport: Transport { get }
}
Run Code Online (Sandbox Code Playgroud)
然后我想输入erase Transportation并能够存储一个阵列,AnyTransportation其中任何Vehicle一个可以拥有任何东西Motor.
所以这是一个包含3个协议的场景,其中2个具有(嵌套)关联类型.
我不知道该怎么做.实际上,我甚至不知道如何解决更简单的场景:
我们可以将上面的原始场景简化为我们有2个协议的版本,其中只有1个协议具有关联类型:
protocol Vehicle {
var speed: Int { get }
}
protocol Transportation {
associatedtype Transport: Vehicle
var transport: Transport { get }
var name: String { get }
}
Run Code Online (Sandbox Code Playgroud)
然后我们说我们有一个Bus符合Vehicle:
struct Bus: Vehicle {
var speed: Int { return 60 }
}
Run Code Online (Sandbox Code Playgroud)
然后我们有两个不同的巴士线路,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
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以Transportation使用base和box模式和类来键入erase ,如bignerdranch所述:
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") }
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以放入RedBusLine或BlueBusLine放入
let busRides: [AnyTransportation<Bus>] = [AnyTransportation(RedBusLine()), AnyTransportation(BlueBusLine())]
busRides.forEach { print($0.name) } // prints "Red line\nBlue line"
Run Code Online (Sandbox Code Playgroud)
在关于与上面链接的类型擦除的博客文章中,我想要的实际上是一种解决方法Homogeneous Requirement.
想象一下,我们有另一个Vehicle,例如a Ferry和a FerryLine:
struct Ferry: Vehicle {
var speed: Int { return 40 }
}
struct FerryLine: Transportation {
let transport: Ferry = Ferry()
var name = "Ferry line"
}
Run Code Online (Sandbox Code Playgroud)
我想我们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'
Run Code Online (Sandbox Code Playgroud)
当然这不起作用......因为AnyTransportation期望传递符合的类型Transportation,但AnyVehicle当然不符合它.
但我无法找到解决方案.有没有?
[AnyTransportation<AnyVehicle>]?下面仅详细说明我希望通过原始场景实现的目标
我原来需要的是把任何Transportation具有任何Vehicle,这本身有任何Motor同一阵列内:
let transportations: [AnyTransportation<AnyVehicle<AnyMotor>>] = [BusLine(), FerryLine()] // want to put `BusLine` and `FerryLine` in same array
Run Code Online (Sandbox Code Playgroud)
如果你想用任何带有任何引擎的车辆表达任何交通工具,那么你需要 3 个盒子,每个盒子都用“以前”打字擦除的包装纸说话。你不希望任何这些框的通用占位符,只要你想在谈话完全异质的情况下的术语(如不与任何运输的具体 Vehicle类型,或与任何车辆特定 Motor类型)。
此外,您可以使用闭包代替使用类层次结构来执行类型擦除,这允许您捕获基本实例而不是直接存储它。这允许您从原始代码中删除大量样板。
例如:
protocol Motor {
var power: Int { get }
}
protocol Vehicle {
associatedtype Engine : Motor
var engine: Engine { get }
}
protocol Transportation {
associatedtype Transport : Vehicle
var transport: Transport { get }
var name: String { get set }
}
// we need the concrete AnyMotor wrapper, as Motor is not a type that conforms to Motor
// (as protocols don't conform to themselves).
struct AnyMotor : Motor {
// we can store base directly, as Motor has no associated types.
private let base: Motor
// protocol requirement just forwards onto the base.
var power: Int { return base.power }
init(_ base: Motor) {
self.base = base
}
}
struct AnyVehicle : Vehicle {
// we cannot directly store base (as Vehicle has an associated type).
// however we can *capture* base in a closure that returns the value of the property,
// wrapped in its type eraser.
private let _getEngine: () -> AnyMotor
var engine: AnyMotor { return _getEngine() }
init<Base : Vehicle>(_ base: Base) {
self._getEngine = { AnyMotor(base.engine) }
}
}
struct AnyTransportation : Transportation {
private let _getTransport: () -> AnyVehicle
private let _getName: () -> String
private let _setName: (String) -> Void
var transport: AnyVehicle { return _getTransport() }
var name: String {
get { return _getName() }
set { _setName(newValue) }
}
init<Base : Transportation>(_ base: Base) {
// similar pattern as above, just multiple stored closures.
// however in this case, as we have a mutable protocol requirement,
// we first create a mutable copy of base, then have all closures capture
// this mutable variable.
var base = base
self._getTransport = { AnyVehicle(base.transport) }
self._getName = { base.name }
self._setName = { base.name = $0 }
}
}
struct PetrolEngine : Motor {
var power: Int
}
struct Ferry: Vehicle {
var engine = PetrolEngine(power: 100)
}
struct FerryLine: Transportation {
let transport = Ferry()
var name = "Ferry line"
}
var anyTransportation = AnyTransportation(FerryLine())
print(anyTransportation.name) // Ferry line
print(anyTransportation.transport.engine.power) // 100
anyTransportation.name = "Foo bar ferries"
print(anyTransportation.name) // Foo bar ferries
Run Code Online (Sandbox Code Playgroud)
请注意,AnyMotor尽管Motor没有任何关联类型,我们仍然构建。这是因为协议不符合自身,所以我们不能使用Motor自身来满足Engine关联类型(需要: Motor)——我们目前必须为它构建一个具体的包装器类型。
| 归档时间: |
|
| 查看次数: |
525 次 |
| 最近记录: |