Ios 无法在Swift中的另一协议中将协议用作associatedtype

Ios 无法在Swift中的另一协议中将协议用作associatedtype,ios,swift,swift-protocols,associated-types,Ios,Swift,Swift Protocols,Associated Types,我有一个协议,Address,它继承了另一个协议,Validator,并且Address满足扩展中的Validator要求 还有另一个协议,FromRepresentable,它有一个关联类型(ValueWrapper)要求,应该是验证器 现在,如果我尝试将地址用作关联类型,则它不会编译。上面说, 推断类型“地址”(通过匹配要求“valueForDetail”)为 无效:不符合“验证程序” 这种用法违法吗?难道我们不能用地址代替验证器,因为所有地址都是验证器 下面是我正在尝试的一段代码 enum

我有一个协议,
Address
,它继承了另一个协议,
Validator
,并且
Address
满足扩展中的
Validator
要求

还有另一个协议,
FromRepresentable
,它有一个
关联类型
ValueWrapper
)要求,应该是
验证器

现在,如果我尝试将
地址
用作
关联类型
,则它不会编译。上面说,

推断类型“地址”(通过匹配要求“valueForDetail”)为 无效:不符合“验证程序”

这种用法违法吗?难道我们不能用
地址
代替
验证器
,因为所有
地址
都是
验证器

下面是我正在尝试的一段代码

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
        }
    }
}

更新:提交了一份

您有几个问题:

首先,您实际上并没有声明地址实现了验证器

//Address inherits Validator
protocol Address : Validator {
    var addressLine1: String {get set}
    var city: String {get set}
    var country: String {get set}
}
并且不声明ValueWrapper的关联类型:

typealias ValueWrapper = ShippingAddress
而且您似乎实际上希望AddressFrom.valueForDetail获取一个
发货地址

func valueForDetail(valueWrapper: ShippingAddress) -> String {
    switch self {
    case .Address1:
        return valueWrapper.addressLine1
    case .City:
        return valueWrapper.city
    case .Country:
        return valueWrapper.country
    }
}
总之,它看起来像:

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 implicity 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

    // define associated type for FormRepresentable
    typealias ValueWrapper = ShippingAddress
    func valueForDetail(valueWrapper: ShippingAddress) -> String {
        switch self {
        case .Address1:
            return valueWrapper.addressLine1
        case .City:
            return valueWrapper.city
        case .Country:
            return valueWrapper.country
        }
    }
}
问题是,一旦将协议的
关联类型
约束到特定(非
@objc
)协议,就必须使用具体类型来满足该要求

这是因为–因此,这意味着您不能使用
地址
来满足符合
验证程序
的类型的协议关联类型要求,因为
地址
不是符合
验证程序
的类型

正如我所演示的,考虑反例:

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
}
最简单的解决方案是在
ValueWrapper
关联类型上放弃
Validator
协议约束,允许您在方法参数中使用抽象类型

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

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

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


嘿,对不起,当我还在努力让它工作时,我发布了这个问题。我编辑了代码片段。地址实际上是从验证器继承的。关于typealias,我认为它应该能够从valueForDetail(93;:Address)函数推断ValueWrapper。我不希望它是concretetype。请注意,在您的实现中,实际上不必显式定义协议的
associatedtype
(通过
typealias
),Swift可以从方法参数类型推断出来。好的,我明白了,但是,我们为什么必须使用concreteType作为带有协议约束的associatedType呢?若我们使用Address作为需要验证器的函数的参数,编译器不会发出任何错误。我不明白,若associatedType具有协议约束,为什么我们必须使用具体类型。它是否与不变性行为或内存分配有关?你有什么参考资料可以让我提供更多的信息吗?@VishalSingh我恐怕没有比“因为事情就是这样”更好的理由了——我当然想不出一个不可能的理由,因为无约束的版本很好用。我在swift evolution邮件列表或bug追踪器中似乎也找不到与此相关的任何信息。这可能很值得一提,看看他们怎么说。谢谢你,我会这样做,如果我得到什么,我会更新这篇文章。在那之前,我会记住这一点:)很乐意帮忙,我当然会对回复感兴趣:)
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
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))