本文讲的是Swift 2 中为实存类型和泛型搭桥牵线,

我们又回到了讨论泛型的另一个章节,来讨论泛型,有其他类型的协议和在 Swift 2 中的其他类型的系统限制。这次我们会深入讨论一个有趣的变通方法,它是那个声名狼藉的 jckarter 教会我的。我们也会讨论在未来的 Swift 版本中,这个变通方法通过增强型的实存类型就变得不必要了。

Swift 中的实存类型

一般而言,实存类型允许我们去使用类型的需求来定义类型变量。我们可以在整个项目中使用这些类型变量,它可以不需要被知道背后是具体的哪个类型实现这些需求的。

在 Swift 2 中,只有使用 protocol<> 语法 ( 在 Swift 3 中会被 & 语法替换 ) 才能定义一个实存类型。

通过定义一个方法函数,它需要使用一个实存类型参数,我们能在不知道参数的具体类型的情况下,使用实存类型中任意的一个:

    protocol Saveable {func save()}protocol Loadable {func load()}func doThing(thing: protocol<Saveable, Loadable>) {thing.save()thing.load()}

在许多实存类型的实现方式上,他们与泛型都很想象。为什么我们选择这个而不是其他?我的一个朋友 Russ Bishop 在他的博客上发布过一个文章,详细的讨论了实存类型和泛型 - 如果你对其中的细节好奇,你应该去读一读它 !

为实存类型和泛型搭桥牵线

在一个 之前的博客文章中 我指出了一些类型信息上的不一致性,泛型是静态的,它在编译的时候类型就确定了,实存类型在运行时候才能确定,这意味着类型的信息是动态的。

今天,我想把注意力都放在一个具体的例子上(虽然会很简单),我们在 PlanGrid 应用程序中遇到过。

作为我们的 客户端-服务端 同步过程的一部分,我们把从 JSON 解析出的内容持久化存储在我们的数据库中。我们通过泛型类数据访问这些对象来实现。数据访问对象有一个泛型类参数,它指定了这个将要被持久化保存的对象的类型。

在我们简单的例子中,我们需要去持久化保存 CatDog 和 Cow 实例。

    protocol PersistedType {}// Types that will be persistedclass Cat: PersistedType {}class Dog: PersistedType {}class Cow: PersistedType {}// DAO that provides a generic persistence mechanismclass GenericDAO<ObjectType: PersistedType> {func save(objectType: ObjectType) {print("Saved \(objectType) in \(ObjectType.self) DAO")}}

在 PlanGrid 应用程序中,我们有一个协调点,它对我们泛型 DAO(数据访问对象) 所有特殊的实例都有一个引用。在同步过程中,我们遇到了一系列不同的类型,它们需要被准确的存储进泛型 DAO(数据访问对象) 类型。(比如,cows 应该被通过GenericDAO<Cow> 来存储。)

考虑到一个不同实例的异构列表,基于我们遇到的对象类型,我们想自动的查询,调用 DAO(数据访问对象) 进行持久化存储。

    // A list of our generic data storeslet genericDAOs: [Any] = [GenericDAO<Cat>(), GenericDAO<Dog>(), GenericDAO<Cow>()]// A list of instances we have parsed & need to persistlet instances: [PersistedType] = [Cat(), Dog(), Cow()]

我们怎样实现一个有关迭代所有在 实例 中的元素并且保存他们进入泛型 DAO(数据访问对象)的循环,它有我们想要保存的匹配的类型参数。理论上,我们想按照以下内容实施(在 Swift 2 语法中是非法的):

    // `element` is an existential since we don't know the concrete type// we only know it conforms to the `PeristedType` protocol.for element in instances {// Invalid! Cannot use existential type as generic type parameterfor case let dao as? GenericDAO<element.Self> in genericDAOs {dao.save(element)}}

一些潜在的,对 Swfit 未来的提高能让这个方法实现,但是目前,我们不能对实存类型进行动态的引用(element.Self) 和作为一个泛型类型参数来使用它。

变通方法

.Self 成员变量,引用到某个实存类型的具体类型并不在 Swift 2 中存在。然而,我们能通过协议和扩展协议,使用 Self来访问这个具体的实存类型。

使用这个聪明的反向控制,我们能配合 PersistedType 协议使用这个 Self 类型 (那些所有持久类实现的),对我们的GenericDAO<T> 动态的指定泛型类参数。

    extension PersistedType {// Pass in a list of all DAOs.func saveInCorrectDAO(potentialDAOs: [Any]) {// Iterate until we find GenericDAO with type parameter that matches// our existential type.for case let dao as GenericDAO<Self> in potentialDAOs {dao.save(self)}}}// ...for element in instances {element.saveInCorrectDAO(genericDAOs)}

通过这个协议的扩展,我们能成功使用这个底层的实存类型 (Self),作为一个泛型类型的参数。虽然这个反向控制流根本不漂亮,但是这个变通方法非常有用,它很好的缩小了实存类型和泛型之间的隔阂。

前途是光明的

在许多其他重要的改进之间,增强型的实存类型方案 草稿 将会替代这个变通方法,通过允许通过 .Self 对底层的一个实存类型进行引用,并且让这个类型作为一个泛型类参数变得可能。

虽然这个增强型实存类型的方案仍然处于紧锣密鼓的开发中,但它仍旧值得一读。如果这是最后一个我们今天将讨论的方案,我们会缩小实存类型和泛型之间的隔阂。更重要的是,配合其他类型的协议一起工作将再也不是难事 - 可能这是自从 Swift面世以来最重要的改进了。

对突破 Swift 的极限感兴趣么? 我们需要你!

参考文献:

  • 增强型实存类型 草稿 ( Enhanced Existentials Proposal Draft ) - 草稿方案,它正在被精雕细琢并且展示出很多对 Swift 的实存类型的巨大改进。
  • 泛型的声明 ( Generics Manifesto ) - Doug Gregor 的最初的 Swift 实现 - 发展/演进 邮件中展示出了很多对 Swift 的泛型的潜在的改进(包括增强型实存类型)。
  • 有实存类型的抽象类型 ( Abstract Types Have Existential Type ) - 这片文章是有关总结了在各种编程语言中,实现实存类型的想法。对我理解什么是实存类型最有关系的句子是: “实存类型提供了足够多的信息用于验证匹配条件 [...],没有提供任何有关显示载体的信息或者实现如何操作的算法。”




原文发布时间为:2016年08月02日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。

Swift 2 中为实存类型和泛型搭桥牵线相关推荐

  1. Swift中的可选类型(Optional)

    为什么80%的码农都做不了架构师?>>>    Swift中的可选类型(Optional) ##什么是Optional Optional在Swift中表示可选类型,那么什么是可选类型 ...

  2. java dog类型_java泛型中?和T有什么区别?

    public static void printColl(ArrayList> al){ Iterator> it = al.iterator(); while(it.hasNext()) ...

  3. kotlin 扩展函数_在 Kotlin 中“实现”trait/类型类

    本文原发于我的个人博客:https://hltj.me/kotlin/2020/01/11/kotlin-trait-typeclass.html.本副本只用于知乎,禁止第三方转载. trait 与类 ...

  4. Swift 3 中的新特性

    原文:What's New in Swift 3? 作者: Ben Morrow 译者:kmyhy Swift 3 将在下半年推出,对于 Swift 程序员来说,它带来了许多改变. 如果你没有密切关注 ...

  5. 对比两个同类型的泛型集合并返回差异泛型集合 ——两个List类名的比较

    1: /// <summary> 2: /// 对比两个同类型的泛型集合并返回差异泛型集合 3: /// </summary> 4: /// <typeparam nam ...

  6. 为什么要在JavaScript中使用静态类型? (使用Flow进行静态打字的4部分入门)

    by Preethi Kasireddy 通过Preethi Kasireddy 为什么要在JavaScript中使用静态类型? (使用Flow进行静态打字的4部分入门) (Why use stati ...

  7. 避免在Swift Struct中使用闭包

    为什么我们应该避免在结构体使用闭包 我们所有人都喜欢闭包,你难道不喜欢吗? Closure能够让iOS开发者生活更轻松.如果它让我们更轻松了,那为啥我还要说不在在结构体中使用闭包了,原因就是:&quo ...

  8. 如何在Swift 3中创建调度队列

    在Swift 2中,我能够使用以下代码创建队列: let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue&quo ...

  9. ​Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数

    ​Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数 7.4.4  为外部参数设置默认值 开发者也可以对外部参数设置默认值.这时,调用的时候,也可以省略参数传递本文选自Swift ...

最新文章

  1. 面向服务的计算(SOC)课程相关资料
  2. boost::multiprecision模块debug_adaptor相关的测试程序
  3. scala教程之:可见性规则
  4. colab加载google drive并且调试时跳过不运行
  5. ReflectionClass与Closure
  6. spark wai_WAI-ARIA对自动完成小部件的支持
  7. gb28181协议java_gb28181开发源码
  8. 佳能Canon MF4700 一体机驱动
  9. java 文字水印 旋转_java实现倾斜水印铺满整张图
  10. 2k21sports服务器暂时不可用,NBA2K20服务器不可用怎么解决 nba2k20进不去游戏解决办法...
  11. FLINK任务重启 Streaming File Sink落地hdfs的中间状态In-progress格式文件处理方案
  12. 网络安全笔记5——数字签名
  13. unity如何插入图片_Unity3D教程:导入贴图和模型
  14. 最新课表 | 谱尼学院十一月培训课程重磅来袭!
  15. 使用matlab建立个人简历,HTML 使用表格制作简单的个人简历
  16. 怎样自己制作一个小程序?新手必知流程!
  17. Parallels Desktop 安装Win 10提示“安全启动功能防止操作系统启动”该怎么解决?
  18. 【ZZULIOJ】1011: 圆柱体表面积
  19. 法国西南部发生火车追尾事故 已造成至少40人伤
  20. 强化学习 马尔科夫决策过程(MDP)

热门文章

  1. 魅族 刷机android 6.0,乐视X800+安卓6.0.1 魅族Flyme6刷机包 最新6.7.12.29R版 紫火20180510更新...
  2. 百度坐标批量转换成WGS84坐标
  3. 通过修改程序解决win7下应用程序兼容性助手弹出
  4. modis遥感影像数据批量下载(不限制网速的办法)
  5. 《那些年啊,那些事——一个程序员的奋斗史》——50
  6. 中国第一批程序员的“青春饭”已经恰完了,35+的程序员该何去何从?
  7. Eclipse rap 开发经验总结
  8. 轻便易用的三维建模软件
  9. 宝瓷林【名贵釉系列】宫廷秘釉“茶叶末”
  10. 城堡争霸服务器维护,城堡争霸 - 阵营守护神(国际服)无法连接服务器是什么原因...