Swift 4 Generics —通用类,其变量符合与关联的协议一致的…

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’”)。

该解决方案除了不能满足我们的需求之外,还使用了强制转换,因此使用时间更长,并且使我们的代码难以理解。