注册

探讨SWIFT 5.2的新功能特性

从表面上看,SWIFT 5.2在新的语言特性方面肯定是一个小版本,因为这个新版本的大部分重点是提高SWIFT底层基础结构的速度和稳定性,例如如何报告编译器错误,以及如何解决构建级依赖。

然而,斯威夫特5.2总数新的语言特性可能相对较小,它确实包括两个新功能,它们可能会对SWIFT的整体功能产生相当大的影响。函数式程序设计语言.

本周,让我们探讨这些特性,以及我们如何可能使用它们来接受一些在函数式编程世界中非常流行的不同范例--在面向对象的SWIFT代码库中,它们可能会感觉更加一致和熟悉。

在我们开始之前,作为Xcode 11.4的一部分,SWIFT5.2仍然处于测试版,请注意,本文是一篇非常探索性的文章,代表了我对这些新语言特性的第一印象。随着我在生产中使用新特性获得更多经验,我的观点可能会发生变化,尽管我将尝试在这种情况下更新这篇文章,但我建议您使用本文作为灵感,亲自探索这些新特性,而不是直接使用以原样呈现的解决方案。

有了这个小小的免责声明,让我们开始探索吧!

一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431, 分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。

调用类型为函数

尽管SWIFT并不是一种严格的函数式编程语言,但毫无疑问,函数在其总体设计和使用中扮演着非常重要的角色。从闭包如何作为异步回调使用,到集合如何大量使用典型的函数模式(如map和reduce-职能无处不在。

SWIFT5.2的有趣之处在于它开始模糊函数和类型之间的界限。尽管我们一直能够将任何给定类型的实例方法作为函数传递(因为SWIFT支持一级函数),我们现在能够调用某些类型,就好像它们本身是函数一样。.

让我们先来看看一个使用Cache我们内置的类型“SWIFT中的缓存”-这提供了一个更多的“快速友好”包装上的APINSCache:

class Cache {
private let wrapped = NSCache()
private let dateProvider: () -> Date
private let entryLifetime: TimeInterval

...

func insert(_ value: Value, forKey key: Key) {
...
}
},>

假设我们想要向上面的类型添加一个方便的API--让我们自动使用插入值的id作为它的缓存键,以防当前Value类型符合标准库的Identifiable协议。虽然我们可以简单地命名新的apiinsert还有,我们要给它起一个非常特别的名字-callAsFunction:

extension Cache where Value: Identifiable, Key == Value.ID {
func callAsFunction(_ value: Value) {
insert(value, forKey: value.id)
}
}

这似乎是一种奇怪的命名约定,但通过这样命名我们的新方便方法,我们实际上已经给出了Cache输入一个有趣的新功能--它现在可能被称为函数--如下所示:

let document: Document = ...
let cache = Cache()

// We can now call our 'cache' variable as if it was referencing a
// function or a closure:
cache(document),>

可以说,这既很酷,也很奇怪。但问题是-它有什么用呢?让我们继续探索,看看DocumentRenderer协议,它为用于呈现的各种类型定义了一个公共接口。Document应用程序中的实例:

protocol DocumentRenderer {
func render(_ document: Document,
in context: DocumentRenderingContext,
enableAnnotations: Bool)
}

类似于我们之前向我们的Cache类型,让我们在这里做同样的事情-只是这一次,我们将扩展上面的协议,以允许任何符合的类型被调用为一个函数,其中包含一组默认参数:

extension DocumentRenderer {
func callAsFunction(_ document: Document) {
render(document,
in: .makeDefaultContext(),
enableAnnotations: false
)
}
}

上述两个变化在孤立的情况下看起来可能不那么令人印象深刻,但是如果我们将它们放在一起,我们就可以看到为一些更复杂的类型提供基于功能的方便API的吸引力。例如,我们在这里构建了一个DocumentViewController-使用我们的Cache类型,以及基于核心动画的DocumentRenderer协议--在加载文档时,这两种协议现在都可以简单地作为函数调用:

class DocumentViewController: UIViewController {
private let cache: Cache
private let render: CoreAnimationDocumentRenderer

...

private func documentDidLoad(_ document: Document) {
cache(document)
render(document)
}
},>

这很酷,特别是如果我们的目标是轻量级API设计或者如果我们在建造某种形式的领域专用语言。虽然通过传递实例方法来实现类似的结果一直是可能的好像它们是封闭的-通过允许直接调用我们的类型,我们都避免了手动传递这些方法,并且能够保留API可能使用的任何外部参数标签。

例如,假设我们还想做一个PriceCalculator变成一个可调用的类型。为了维护原始API的语义,我们将保留for外部参数标签,即使在声明callAsFunction执行情况-如下:

extension PriceCalculator {
func callAsFunction(for product: Product) -> Int {
calculatePrice(for: product)
}
}

下面是上述方法与存储对类型的引用的比较calculatePrice方法-请注意第一段代码是如何丢弃参数标签的,而第二段代码是如何保留参数标签的:

// Using a method reference:
let calculatePrice = PriceCalculator().calculatePrice
...
calculatePrice(product)

// Calling our type directly:
let calculatePrice = PriceCalculator()
...
calculatePrice(for: product)

让类型像函数一样被调用是一个非常有趣的概念,但也许更有趣的是,它还使我们能够走相反的方向--并将函数转换为适当的类型。

面向对象的函数式编程
虽然在许多函数式编程概念中有着巨大的威力,但当使用大量面向对象的框架(就像大多数Apple的框架一样)时,应用这些概念和模式往往是很有挑战性的。让我们看看SWIFT5.2的新可调用类型功能是否可以帮助我们改变这种状况。
后续精彩内容请转到我的博客继续观看

转自:https://www.jianshu.com/p/c8f0f4ae63e5

0 个评论

要回复文章请先登录注册