Swift协议要求属性作为协议

时间:2016-06-24 07:27:47

标签: swift protocols

我正在尝试定义一个协议" Repository"这需要定义一些属性(实现特定协议" DataSource")

但由于我的真实场景的复杂性,其中一个属性需要是" DataSource"的子协议。

我将问题简化为这个简单的代码:

protocol DataSource { }

protocol ExtraDataSouce: DataSource {
    func method1() -> String
}

struct MyDataSource: ExtraDataSouce {
    func method1() -> String {
        return "whatever"
    }
}


protocol Repository {
    var firstDataSource: DataSource { get }
    var secondDataSource: DataSource { get }
}

struct MyRepository: Repository {
    var firstDataSource: DataSource
    var secondDataSource: MyDataSource
}

在编译时返回错误,因为" MyRepository"不符合"存储库"。但我认为它确实存在......任何关于它为什么不接受" secondDataSource"在" MyRepository"定义为" MyDataSource" ?

2 个答案:

答案 0 :(得分:2)

搜索后,我发现了有关您问题的这些信息(如果我错了,或者我错过了某些内容,请纠正我):

即使在逻辑上你的代码应该工作,当你在MyRepository类中声明它们的类型时,当你使用常规或只读协议变量时,swift编译器不会分开这些情况。换句话说,如果您要写入Repository

,代码中的错误就会变得明显
var secondDataSource: DataSource { get set }

并且编译器不会分开这种情况。我没有找到完全正确的方法来做你想做的事。但有两种接近的方式:

1)明显且可能是最正确的方式 - 在secondDataSource中更改MyRepository类型,并根据需要使用其他变量:

var _secondDataSource: MyDataSource
var secondDataSource: DataSource {
        get {return _secondDataSource}
        set {
            guard let newValue = newValue as? MyDataSource else {
                fatalError("MyRepository: attempt to set DataSource type, MyDataSource type expected")
            }
            _secondDataSource = newValue
        }
    }

2)协议方式的关联类型。在这里,我将改进@RaduNunu答案,因为他的代码中的associatedtype type = DataSource行只有占位符效应,而他的解决方案允许您选择采用任何类型的secondDataSource,例如:String

protocol Repository {
    associatedtype Type = DataSource
    var firstDataSource: DataSource { get }
    var secondDataSource: Type { get }
}

struct MyRepository: Repository {
    var firstDataSource: DataSource
    var secondDataSource: String // - this is ok!
}

这段代码将编译并运行,但看起来非常糟糕。而是键入占位符,您最好使用协议一致性:

protocol Repository {
    associatedtype Type: DataSource
    var firstDataSource: DataSource { get }
    var secondDataSource: Type { get }
}

struct MyRepository: Repository {
    var firstDataSource: DataSource
    //var secondDataSource: String - this line not allowed now
    var secondDataSource: MyDataSource
}

此代码已经非常接近目标。但是,从现在开始,您无法将协议用作关联类型,因此声明

var secondDataSource: DataSource
MyRepository中的

将无效。这是使用关联类型的代价:您只能使用DataSource一致的类/枚举/结构类型。

答案 1 :(得分:1)

您的Repository协议实现了DataSource类型的2变量,当您尝试修改符合Repository协议的struct中的变量类型时,它不会因为所需的类型而执行此操作。您应该使用关联类型进行此更改

protocol Repository {
    associatedtype type = DataSource
    var firstDataSource: DataSource { get }
    var secondDataSource: type { get }
}