
Swift 4泛型—泛型类,其变量符合具有关联类型的协议。
很多时候,我发现自己在为Swift的通用协议和相关类型苦苦挣扎。
每次我想“ POPify”一个功能时,我脑海中都会想起一件事,而在编码时,我遇到了一些意想不到的奇怪错误和语言限制。
上周,我们团队中的一个人向我询问了有关此主题的信息,我们发现自己尝试了许多变通办法以使此工作可行。
他想要的是: 一个通用类,该类的变量符合具有关联类型的协议。
这听起来可能很奇怪,所以让我们深入了解它,您会明白的。
准备一个快速的操场和..
以这种情况为例:我想制定一些数据提供者的协议。 该协议具有单个功能,可以提供该数据:
协议DataProvider {
func GiveData()->任何
}
我想要一个使用数据的类:
类DataConsumer {
var数据:可以吗?
var dataProvider:DataProvider?
func getData(){
self.data = self.dataProvider?.giveData()
}
}
就目前而言,一切似乎还不错。
但是 ,如果我们希望那对夫妇(Giver和消费者)使用相同类型的数据怎么办? 听起来是Swift惊人功能之一- 泛型的理想选择。
使用符合某种协议的关联类型(’T’)声明DataConsumer。 然后,DataConsumer的’ dataProvider’var是符合DataProvider的类型,并且具有在DataConsumer中声明的相同关联类型(’T’)。
这可能很好。 想象一下初始化DataConsumer及其在外部作用域中的数据类型,然后在尝试分配dataGiver时,自动完成功能将提供您在初始化使用者时声明的类型的完成。

让我们尝试一下
让我们首先为数据类型创建一个协议:
协议DataType {}
现在,我们要让给予者和消费者使用相同的类型。
让我们编辑数据使用者并将其设为通用类型。
类DataConsumer {
var数据:T?
var dataProvider:DataProvider?
func getData(){
self.data = self.dataProvider?.giveData()
}
}
编译器现在会喊出GiveData()返回Any而不是T。
让我们编辑DataProvider:
协议DataProvider {
关联类型T:数据类型
func GiveData()-> T
}
现在再次回到我们的DataConsumer来修复我们的dataProvider变量,使其具有诸如DataConsumer的关联类型:
var dataProvider:DataProvider ?
糟糕…编译器现在说:
无法专门化非通用类型“ DataProvider”
我个人不立即理解此错误,不得不搜索互联网。
现在,编译器提供了针对该错误的解决方案(Xcode 9):
将“ ”替换为“
首先,什么是“?” 那是一个奇怪的错误。
其次,单击“修复”即可:
var dataProvider:DataProvider?
现在,编译器抱怨其他问题:
协议“ DataProvider”只能用作通用约束,因为它具有“自身”或相关联的类型要求

威尔 如果是这样,让我们尝试另一种方式。 让我们让函数具有通用参数,然后删除associatedtype声明,而不是为协议声明关联的类型:
func GiveData ()-> T
似乎编译器现在根本没有抱怨。
让我们尝试使用我们现在拥有的……
让我们创建一个JSON使用者,给予者和数据类型:
结构JSONData:DataType {}
最后一个类JSONDataProvider:DataProvider {
func GiveData ()-> T {
返回JSONData()
}
}
编译器现在将再次尖叫:
无法将类型“ JSONData”的返回表达式转换为类型“ T”
什么? 但是我们在协议中写道T是DataType类型,而JSONData结构符合DataType ..?
好吧,我们可以去做,就像Xcode提供的那样:
返回JSONData()为! Ť
丑陋的地狱,但可能行得通..
在继续之前,请添加此扩展名,以帮助我们识别类型:
扩展数据类型{
func whoAmI(){
打印(type(of:self))
}
}
现在,在JSONDataProvider类解密之后,添加以下行:
让dataConsumer = DataConsumer ()
dataConsumer.dataProvider = JSONDataProvider()
dataConsumer.getData()
dataConsumer.data?.whoAmI()
oo! 控制台显示:
JSONData

好吧,我们还有另一个问题。 在对whoAmI()的调用之后立即添加以下行:
dataConsumer.dataProvider = XMLDataProvider()
这样可以成功编译,这不是想要的。
显然,现在调用getData()会导致崩溃。 您可以尝试一下。
那是因为我们不能强制使用特定的DataGiver类型(记住吗?“不能专门化非通用类型’DataProvider’”)。
该解决方案除了不能满足我们的需求之外,还使用了强制转换,因此使用时间更长,并且使我们的代码难以理解。