ios - 无法在 Swift 的另一个协议(protocol)中将协议(protocol)用作关联类型

标签 ios swift swift-protocols associated-types

我有一个协议(protocol) Address,它继承自另一个协议(protocol) Validator,而 Address 实现了 Validator 扩展中的要求。

还有另一个协议(protocol),FromRepresentable,它有一个associatedType (ValueWrapper) 要求,它应该是Validator .

现在,如果我尝试将 Address 用作 associatedType,则它不会编译。它说,

Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform to 'Validator'.

这种用法违法吗?我们不应该使用 Address 代替 Validator,因为所有 Address 都是 Validator

下面是我正在尝试的代码。

enum ValidationResult {
    case Success
    case Failure(String)
}

protocol Validator {
    func validate() -> ValidationResult
}

//Address inherits Validator
protocol Address: Validator {
    var addressLine1: String {get set}
    var city: String {get set}
    var country: String {get set}
}

////Fulfill Validator protocol requirements in extension
extension Address {
    func validate() -> ValidationResult {
        if addressLine1.isEmpty {
            return .Failure("Address can not be empty")
        }
        return .Success
    }
}

protocol FormRepresentable {
    associatedtype ValueWrapper: Validator
    func valueForDetail(valueWrapper: ValueWrapper) -> String
}


// Shipping Address conforming to Address protocol. 
// It should also implicitly conform to Validator since
// Address inherits from Validator?
struct ShippingAddress: Address {
    var addressLine1 = "CA"
    var city = "HYD"
    var country = "India"
}


// While compiling, it says:
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform
// to 'Validator'.
// But Address confroms to Validator.
enum AddressFrom: Int, FormRepresentable {
    case Address1
    case City
    case Country

    func valueForDetail(valueWrapper: Address) -> String {
        switch self {
        case .Address1:
            return valueWrapper.addressLine1
        case .City:
            return valueWrapper.city
        case .Country:
            return valueWrapper.country
        }
    }
}

更新:提交了 bug.

最佳答案

问题,这David has already alluded to ,是一旦您将协议(protocol)的 associatedtype 限制为特定(非 @objc)协议(protocol),您就必须使用具体类型来满足该要求。

这是因为protocols don't conform to themselves – 因此意味着您不能使用 Address 来满足协议(protocol)对符合 Validator 的类型的关联类型要求,因为 Address不是符合 Validator 的类型。

正如我演示的那样in my answer here ,考虑反例:

protocol Validator {
    init()
}
protocol Address : Validator {}

protocol FormRepresentable {
    associatedtype ValueWrapper: Validator
}

extension FormRepresentable {
    static func foo() {
        // if ValueWrapper were allowed to be an Address or Validator,
        // what instance should we be constructing here?
        // we cannot create an instance of a protocol.
        print(ValueWrapper.init())
    }
}

// therefore, we cannot say:
enum AddressFrom : FormRepresentable {
    typealias ValueWrapper = Address
}

最简单的解决方案是放弃 Validator 关联类型上的 Validator 协议(protocol)约束,允许您在方法参数中使用抽象类型。

protocol FormRepresentable {
    associatedtype ValueWrapper
    func valueForDetail(valueWrapper: ValueWrapper) -> String
}

enum AddressFrom : Int, FormRepresentable {

    // ...

    func valueForDetail(valueWrapper: Address) -> String {
        // ...
    }
}

如果你需要关联的类型约束,并且每个 AddressFrom 实例只需要一个 Address 的具体实现作为输入——你可以使用泛型来实现你的 AddressFrom 将使用您的方法中使用的给定具体地址类型进行初始化。

protocol FormRepresentable {
    associatedtype ValueWrapper : Validator
    func valueForDetail(valueWrapper: ValueWrapper) -> String
}

enum AddressFrom<T : Address> : Int, FormRepresentable {

    // ...

    func valueForDetail(valueWrapper: T) -> String {
        // ...
    }
}

// replace ShippingAddress with whatever concrete type you want AddressFrom to use
let addressFrom = AddressFrom<ShippingAddress>.Address1

但是,如果您同时需要关联的类型约束 ,每个 AddressFrom 实例必须能够处理任何类型的 Address 的输入– 您将使用类型删除来将任意 Address 包装在具体类型中。

protocol FormRepresentable {
    associatedtype ValueWrapper : Validator
    func valueForDetail(valueWrapper: ValueWrapper) -> String
}

struct AnyAddress : Address {

    private var _base: Address

    var addressLine1: String {
        get {return _base.addressLine1}
        set {_base.addressLine1 = newValue}
    }
    var country: String {
        get {return _base.country}
        set {_base.country = newValue}
    }
    var city: String {
        get {return _base.city}
        set {_base.city = newValue}
    }

    init(_ base: Address) {
        _base = base
    }
}

enum AddressFrom : Int, FormRepresentable {

    // ...

    func valueForDetail(valueWrapper: AnyAddress) -> String {
        // ...
    }
}

let addressFrom = AddressFrom.Address1

let address = ShippingAddress(addressLine1: "", city: "", country: "")

addressFrom.valueForDetail(AnyAddress(address))

关于ios - 无法在 Swift 的另一个协议(protocol)中将协议(protocol)用作关联类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37360114/

相关文章:

ios - 无法访问已安装的 Pod 框架

swift - 如何解决 Swift 协议(protocol)的规范问题

Swift 无法分配 [String : SomeProtocol] to type [String : SomeProtocol] 类型的值

ios - 银联银行 : Basic Loops and Arrays

ios - AdMob 未在 iOS 版本上转换广告。错误代码 8,找不到名称为 : ("com.google.DummyAdapter") 的广告网络适配器

ios - 如何使用 SceneKit 实现 3D 指南针

arrays - 如何创建扩展以允许自定义类型的数组符合协议(protocol)?

ios - UICollectionView 嵌入在 UITableViewCell 中!无法从 tableview 单元格中呈现 viewcontroller!回电

Java 内存管理 : iOS style memory monitoring?

Swift WKWebView 连续拼写检查