删除你的恐惧,类型擦除是这里

188bet金宝搏官网OkCupid是雇用的iOS点击这里了解更多

介绍

在这里,在OkC188bet金宝搏官网upid,我们都在不断努力实现同类最佳斯威夫特的做法。其中一个概念是类型擦除。你可能会想给自己“我从来没有在我的生活中使用类型擦除......”但你错了!没想到,你知道的,AnyObject实际上是擦除的具体类型的协议任何对象。您甚至可能已经看到了任何前缀散落在斯威夫特标准库(AnyHashableAnyIteratorAnyCollection等)。

这种强大的构造是一个双刃剑。一方面,因为所有的类符合AnyObject,它作为所有类型的,如果你需要与未知类型的工作可以是有用的通用包装。在另一方面,它消除了斯威夫特的类型安全系统的依赖。他们不把它称为“安全”没有任何理由!有到知道你正在使用的对象的类型重要的好处,我们都太熟悉的OLE” Objective-C的崩溃从‘无法识别的选择发送到实例’当你调用上未实现一个类型的方法。

但是,莫不是一个两全其美的解决方案?在这里,在OkC188bet金宝搏官网upid我们认为答案是肯定的。我们可以采取类型擦除的力量,并将其应用到具体的使用情况下获得灵活的收藏带来的好处,并仍然保持斯威夫特的稳健型安全系统的刚性。

为了说明起见,我们已经把包括从我们的执行类型擦除的一种特殊形式为我们的会话视图控制器应用程序的代码示例项目。

https://github.com/188bet金宝搏官网OkCupid/swift-type-erasure

iVBORw0KGgoAAAANSUhEUgAABaMAAAszCAYAAACIWkB7AAABgmlDQ1BzUkdCIElFQzYxOTY2LTIu-5

问题

在这个例子中,我们将要建立的是代表了OkCupid iOS应用会话模型。188bet金宝搏官网

当建立了我们的谈话屏幕模型层,我们心中有几个目标:

  1. 我们需要很好的抽象,以支持持续在未来的功能
  2. 所需的模型是用于单元测试容易mockable
  3. 架构应该是可扩展的,允许我们扩展逻辑需要

创建一个简单的结构是我们的第一种方法:

结构谈{让的threadId:字符串让isUnread:BOOL让记者:用户// ..等等}

但是,我们很快意识到,这种做法不符合我们的所有目标。具体而言,这并没有给我们提供所需的可扩展性。例如,如果OkCupid是引入一188bet金宝搏官网个新的会话类型,我们就需要重构大量的代码来支持它。

这些“未来”类型的一些例子可能是PersistingConversationGroupConversationSecretConversation等等。因为会话为混凝土结构,支持多种类型的整个应用程序的对话需要一个显著重构。

因为我们知道,任何类型的对话将共享相同的一套基本属性,使用的协议让我们抽象的具体类型。

该协议导向的方法

让我们创建ConversationProtocol,它包含所有的对话的共享属性的协议。

协议ConversationProtocol {VAR线程ID:字符串{} GET变种记者:USERPROTOCOL {} GET变种isUnread:布尔{GET} //等..}

ConversationProtocol协议是完美的表达共同财产的要求,但是我们也希望一些功能之间共享我们的谈话。例如,我们需要一种方法来区分不同一个对话。幸运的是,雨燕为我们提供了可哈希协议(这从继承Equatable协议)用于此目的。

我们要的是一个实现的可哈希能之间的当前和未来的具体的实现共享ConversationProtocol。然而,这是不可能的协议(ConversationProtocol)实施另一种协议(可哈希)。我们需要一个具体类型

我们可以使用,而不是提供一个协议的默认实现对其他协议哪里声明在扩展,这特别适用于具体的类型:

扩展ConversationProtocol其中自:可哈希{FUNC散列(入散列器:INOUT散列器){hasher.combine(的threadId)} FUNC isEqualTo(_其他:ConversationProtocol) - >布尔{后卫让otherConversation =其它如?自其他{返回FALSE}返回的threadId == otherConversation.threadId}静态FUNC ==(左:自,RHS:自) - >布尔{回报lhs.isEqualTo(右)}}

这告诉编译器的任何具体类型实现ConversationProtocol可哈希得到一个默认的实现是免费的!这是非常强大的,因为它允许一个单一的中央执行可哈希所有的具体类型中ConversationProtocol

要使用它,我们需要做的就是创建一个具体的类型:

结构会话:ConversationProtocol {VAR线程ID:字符串VAR记者:USERPROTOCOL VAR isUnread:BOOL}

现在,用我们的共同实施可哈希所有我们需要做的是:

//这就是魔术发生✨扩展对话:哈希的{}

现在我们有一个具体的会话结构是自动获取我们的执行可哈希只需提供一个空的实现。

但是...这是所有伟大的,直到我们尝试存储我们的实例ConversationProtocol在集...毕竟,我们还是想用抽象类型最终的flexibilty。

VAR conversationSet =设定()

编译器大喊大叫我们...

错误:类型ConversationProtocol'不符合协议“可哈希”

这是有意义的协议不能执行的协议。那么,如何让一组ConversationProtocolS'这是类型擦除的用武之地。

我们需要一个具体类型符合可哈希在一组存储。

结构AnyHashableConversation:ConversationProtocol {VAR线程ID:字符串{回报conversation.threadId} VAR记者:USERPROTOCOL {回报conversation.correspondent} VAR isUnread:布尔{返回conversation.isUnread}私人谈话令:ConversationProtocol的init(谈话:ConversationProtocol){self.conversation=谈话}} //使用我们现有的哈希的实施扩展AnyHashableConversation:哈希的{}

而已!现在,我们可以创建一个集中的任何谈话类型的,它会使用一个共享的实现被散列可哈希

让会话=设定()

结论

使用协议与类型擦除结合的是用于提取共同的功能以及很多具体类型的有用技术。它使我们能够保持我们的代码loosley耦合,可扩展和易于测试。你有经验,使用类型擦除或其他抽象来解决类似的问题?请留下您的想法,我们很想了解更多!

标题图片礼貌:https://www.iteratorshq.com/blog/scala-compiler-phases-with-pictures/type-erasure/