本文由陈云峰翻译,转载请注明。
注意:本教程将使用Swift 4版本捆绑在Xcode 9 beta 1中。
Swift 4是苹果计划于2017年秋季推出的最新版本,其主要重点是提供与Swift 3代码的源兼容性,并努力实现ABI稳定性。
本文重点介绍对Swift的更改将对您的代码产生最大的影响。 而且,让我们开始吧!
Swift 4包含在Xcode 9中。您可以从Apple的开发者门户下载最新版本的Xcode 9(您必须拥有一个活跃的开发者帐户)。 每个Xcode测试版将在发布时捆绑最新的Swift 4快照。
在阅读时,您会注意到[SE-xxxx]格式的链接。 这些链接将带您到相关的Swift Evolution提案。 如果您想了解有关任何主题的更多信息,请务必查看。
我建议您在操场上尝试每个Swift 4功能或更新。 这将有助于巩固您的头脑中的知识,并使您有能力深入了解每个主题。 试图扩大/打破他们的例子来玩弄这些例子。 玩得开心!
注意:本文将针对每个Xcode测试版进行更新。 如果您使用不同的Swift快照,这里的代码不能保证工作。
从Swift 3迁移到4将比从2.2到3更麻烦。一般来说, 大多数变化是相加的,不应该需要大量的个人感觉。 因此,Swift迁移工具将为您处理大部分更改。
Xcode 9同时支持Swift 4以及Swift 3.2中的Swift 3中间版本。 您的项目中的每个目标可以是Swift 3.2或Swift 4,如果需要,您可以逐个迁移。 然而,转换为Swift 3.2并不是完全免费的 – 您可能需要更新代码部分才能与新SDK兼容,并且由于Swift尚未ABI稳定,因此您将需要使用Xcode 9重新编译依赖项。
当您准备迁移到Swift 4时,Xcode再次提供了一个迁移工具来帮助您。 在Xcode中,您可以导航到编辑/转换/到当前Swift语法…以启动转换工具。
选择要转换的目标后,Xcode将提示您对Objective-C推理的偏好。 选择推荐的选项通过限制引用来减少二进制大小(有关此主题的更多信息,请查看下面的限制@objc推断 )
为了更好地了解您的代码中期望的更改,我们将首先介绍Swift 4中的API更改。
在跳转到Swift 4中介绍的补充之前,我们先来看看现有API所做的更改/改进。
String
在Swift 4中获得了很多很好的爱。这个提案包含很多变化,所以让我们分解最大的。 [SE-0163] :
如果你感觉怀旧,字符串再次收藏,就像他们是Swift 2.0之前一样。 此更改消除了对String
上的String
数组的需求。 您现在可以直接在String
对象上进行迭代:
您不仅可以通过String
逻辑迭代,还可以从Sequence
和Collection
获取所有的响铃和口哨:
上面的ASCII示例显示了对Character
。 您现在可以直接从Character
访问Character
。 以前,您需要实例化一个新的String
[SE-0178] 。
另外还有一个是StringProtocol
。 它声明了以前在String
上声明的大部分功能。 这种变化的原因是改善切片如何工作。 Swift 4添加了Substring
类型,用于引用String
上的子序列。
String
和Substring
实现了StringProtocol
使它们具有几乎相同的功能:
// Grab a subsequence of String
let endIndex = galaxy.index(galaxy.startIndex, offsetBy: 3)
var milkSubstring = galaxy[galaxy.startIndex...endIndex] // "Milk"
type(of: milkSubstring) // Substring.Type
// Concatenate a String onto a Substring
milkSubstring += "" // "Milk"
// Create a String from a Substring
let milkString = String(milkSubstring) // "Milk"
另一个很大的改进是String
如何解释图形集合。 此解决方案来自于Unicode 9的改编。以前,由多个代码点组成的Unicode字符会导致count
大于1.常见的情况是具有所选肤色的表情符号。 以下是几个示例,显示前后行为:
这只是“ 字符串宣言”中提到的更改的一个子集。 您可以阅读有关将来希望看到的原始动机和提出的解决方案。
至于Collection
类型, Set
和Dictionary
并不总是最直观的。 幸运的是,斯威夫特队给了他们一些非常需要的爱[SE-0165] 。
基于序列的初始化
列表首先是从一系列键值对(元组)创建一个字典的能力:
重复键处理
您现在可以使用重复的键来处理初始化字典的任何方式。 这有助于避免覆盖键值对,而不会有任何问题:
上面的代码使用zip
和速记+
来通过添加两个冲突的值来解析重复的键。
zip
,您可以在Apple的Swift文档中快速了解它
过滤
Dictionary
和Set
现在都可以将结果过滤到原始类型的新对象中:
字典映射
Dictionary
获得了一个非常有用的方法来直接映射其值:
字典默认值
在Dictionary上访问某个值时,常见的做法是使用nil coalescing运算符给出默认值,以防数值为nil
。 在Swift 4中,这变得更加清洁,并允许您在线突变中做一些真棒:
以前,这种类型的突变将需要在一个blo肿的if-let
语句中包装。 在Swift 4中,可能是一条线!
字典分组
另一个令人惊讶的有用的补充是从Sequence
Dictionary
并将它们分组到桶中的能力:
当通过特定模式对数据进行分组时,这很方便。
预留容量
Sequence
和Dictionary
现在都具有明确保留容量的能力。
// Improved Set/Dictionary capacity reservation
starWordsCount.capacity // 6
starWordsCount.reserveCapacity(20) // reserves at _least_ 20 elements of capacity
starWordsCount.capacity // 24
这些类型的重新分配可能是一项昂贵的任务。 使用reserveCapacity(_:)
是一个简单的方法来提高性能,当您了解需要存储多少数据时。
这是一大堆信息,所以绝对检查这两种类型,并寻找使用这些添加剂来调整代码的方法。
Swift 3的一个元素,一些不太喜欢的是添加fileprivate
。 从理论上讲,这是非常好的,但实际上它的使用往往会令人困惑。 目标是在成员本身中使用private
的,并且在您想要在同一文件中的成员共享访问的情况下很少使用fileprivate
。
问题是Swift鼓励使用扩展将代码分解成逻辑组。 扩展被认为在原始成员声明范围之外,这导致对fileprivate的广泛需求。
Swift 4通过在类型和所述类型的任何扩展之间共享相同的访问控制范围来实现原始意图。 这只适用于相同的源文件[SE-0169] :
这允许您使用fileprivate
作为其预期目的,而不是作为带状编码组织。
现在让我们来看看Swift 4的新功能。这些更改不应该打破你现有的代码,因为它们是简单的加法。
到目前为止,在Swift中,为了序列化和归档您的自定义类型,您必须跳过一些环。对于class
类型,您需要对NSObject
进行子类化并实现NSCoding
协议。
像struct
和enum
这样的值类型需要许多hacks,例如创建一个可以扩展NSObject
和NSCoding
的子对象。
Swift 4通过将序列化到所有三种Swift类型[SE-0166]来解决这个问题:
在这个例子中,您可以看到,使Swift类型可Encodable
和可Decodable
所需的唯一Decodable
是实现可编Codable
协议。 如果所有属性都是Codable
,则协议实现由编译器自动生成。
本文由陈云峰翻译,转载请注明。
要实际编码对象,您需要将其传递给编码器。 Swift编码器正在Swift 4中积极实施。每个编码器根据不同的方案对您的对象进行编码[SE-0167] ( 注意:此提案的一部分仍在开发中):
let jsonEncoder = JSONEncoder() // One currently available encoder
// Encode the data
let jsonData = try jsonEncoder.encode(logSol42)
// Create a String from the data
let jsonString = String(data: jsonData, encoding: .utf8) // "{"sol":42,"discoveries":["rock","rock","rock","rock"]}"
这采取了一个对象,并自动将其编码为JSON对象。 确保查看JSONEncoder
暴露的属性来自定义其输出。
该过程的最后一部分是将数据解码为具体对象:
使用Swift 4编码/解码,您可以在Swift中获得预期的类型安全性,而不依赖于@objc
协议的开销和限制。
到目前为止,您可以参考函数而不调用它们,因为函数是Swift中的闭包。 你不能做的是保持对属性的引用,而不实际访问属性保存的底层数据。
对Swift 4来说,令人兴奋的补充是能够引用类型的关键路径来获取/设置实例的基础值[SE-0161] :
在这里,您将通过设置他们的名字,光剑和主人来创建强制用户的几个实例。 要创建一个关键路径,您只需使用一个反斜杠后跟您感兴趣的属性:
// Create reference to the ForceUser.name key path
let nameKeyPath = \ForceUser.name
// Access the value from key path on instance
let obiwanName = obiwan[keyPath: nameKeyPath] // "Obi-Wan Kenobi"
在这种情况下,您正在为ForceUser
的name
属性创建一个关键路径。 然后,通过将其传递给新的下标keyPath
来使用此键路径。 默认情况下,此下标现在可用于每种类型。
以下是使用关键路径深入到子对象,设置属性和构建关键路径引用的更多示例:
// Use keypath directly inline and to drill down to sub objects
let anakinSaberColor = anakin[keyPath: \ForceUser.lightsaber.color] // blue
// Access a property on the object returned by key path
let masterKeyPath = \ForceUser.master
let anakinMasterName = anakin[keyPath: masterKeyPath]?.name // "Obi-Wan Kenobi"
// Change Anakin to the dark side using key path as a setter
anakin[keyPath: masterKeyPath] = sidious
anakin.master?.name // Darth Sidious
// Note: not currently working, but works in some situations
// Append a key path to an existing path
//let masterNameKeyPath = masterKeyPath.appending(path: \ForceUser.name)
//anakin[keyPath: masterKeyPath] // "Darth Sidious"
Swift的关键路径的美丽在于它们是强类型的! 没有更多的Objective-C字符串风格混乱!
许多编程语言的一个非常常见的特征是能够创建多行字符串文字。 Swift 4通过在三个引号[SE-0168]中包装文本来添加这个简单而有用的语法:
这在构建XML / JSON消息或构建长格式的文本以在UI中显示时非常有用。
为了减少冗长度并提高可读性,标准库现在可以使用单面范围[SE-0172]来推断起始和终点索引。
派上用场的一种方法是创建一个从索引到集合的开始或结束索引的范围:
/ Collection Subscript
var planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
let outsideAsteroidBelt = planets[4...] // Before: planets[4..<planets.endIndex]
let firstThree = planets[..<4] // Before: planets[planets.startIndex..<4]
如您所见,单面范围减少了明确指定开始索引或结束索引的需要。
无限序列
当起始索引为可数类型时,它们还允许您定义无限Sequence
:
模式匹配
单面范围的另一个很好的用途是模式匹配:
下标是使数据类型以简洁方式可访问的重要组成部分。 为了提高其有用性,下标现在可以是通用的[SE-0148] :
在这个例子中,返回类型是通用的。 你可以使用这个通用的下标,如下所示:
返回类型不仅可以是通用的,而且实际的下标类型也可以是通用的:
在这个例子中,你可以看到,传递两个不同的Sequence
类型( Array
和Set
)会导致一个数组的各自的值。
它处理了Swift 4中最大的变化。现在让我们通过一些较小的位和块来更快速地进行一些。
MutableCollection
现在具有mutate方法swapAt(_:_:)
,就像它的声音一样; 交换给定索引值[SE-0173] :
您现在可以使用where
子句来限制关联类型[SE-0142] :
使用协议约束,许多associatedtype
声明可以直接约束其值,而不必跳过环。
最终将其从Objective-C转换为Swift的功能是定义符合类和一组协议的类型的能力[SE-0156] :
要将Objective-C或Swift API公开,请使用@objc
编译器属性。 在许多情况下,Swift编译器为您推断出这一点。 质量推理的三个主要问题是:
@objc
会被推断不明显
Swift 4通过限制@objc
[SE-0160]的推论来解决这个问题。 这意味着在需要Objective-C的完整动态调度功能的情况下,您需要使用@objc
。
您需要进行这些更改的几个示例包括private
方法, dynamic
声明和NSObject
子类的任何方法。
NSNumber
和Swift数字之间已经有很多时髦的行为,这些行为一直困扰着语言太久。 幸运的是,Swift 4压缩了这些错误[SE-0170] 。
以下是一个示例演示示例:
Swift 3中的奇怪行为表明,如果数字溢出,则从0开始。在此示例中,999%2 ^ 8 = 231。
Swift 4通过强制可选的转换来解决问题,只有当数字可以在包含类型中被安全地表达时才返回值。
在过去几个月里,Swift Package Manager已经有了一些更新。 一些最大的变化包括:
这些都是获得SPM所需要的重大步骤。 SPM还有很长的路要走,但是我们可以通过保持积极的建议来帮助形成一个。
有关最近解决的提案的全面了解,请查看“ Swift 4软件包管理器更新” 。
Swift语言多年来一直在增长和成熟。 提案过程和社区参与使得跟踪管道中出现的变化非常容易。 它也使东方任何一个人直接影响演变。
随着Swift 4中的这些变化,我们终于到了一个ABI稳定性就在拐角处的地方。 升级Swift版本的痛苦越来越小。 构建性能和工具大大提高。 在苹果生态系统之外使用Swift变得越来越可行。 并且想一想,我们可能只是从一个直观的实现中完全重写String
的一些;]。
斯威夫特还有更多的东西。 要保持最新的所有更改,请确保查看以下资源:
你对Swift 4有什么想法? 你最喜欢的变化是什么? 你还想从语言中看到什么? 你有没有找到新的和令人兴奋的东西,这里没有涵盖? 让我们在下面的评论中知道!
本文由陈云峰翻译,转载请注明。原文地址