原文:What's New in Swift 3? ,做者:Ben Morrow,译者:kmyhyhtml
Swift 3将于今年下半年推出,为Swift开发者们带来了不少核心代码的改变。若是你没有关注过 Swift Evolution 项目,你可能会好奇Swift 3中有什么改变,它会对你的代码带来什么影响,以及什么时候能够将代码移植到Swift 3。本文就将为你答疑解惑!ios
本文中,我将重点介绍 Swift 3 中的重大改变,以及这些改变对你的代码的深入影响。让咱们开始吧!git
开始程序员
目前,Swift 3 预览版仅在 Xcode 8 beta版中可用。在将来几个月中,Swift 3 仍然在不断改变中,它还会发生一些变化。在 2016 年底,Xcode GM 发布时,Swift 3 的新特性才会稳定。因此各位开发者要沉住气,直到那个时候才能够向 App Store 提交用 Swift 3 编写的 App。github
为了便于开发者们将代码迁移到 Swift 3,苹果在 Xcode 8 中增长了一个 Swift 版本,即 Swift 2.3,对 Swift 做了一个小小的升级。若是你是一个开发者,Swift 2.3 和 2.2 实际并没有不一样,但 Swift 2.3版本可以支持本次WWDC中提到的各类新的SDK 和 Xcode 特性。在 Xcode 8 推出 beta 版时,你能够用 Swift 2.3 来提交你的 app,而不用将代码迁移到 Swift 3。objective-c
我建议你在 Playground 中测试本文讨论的新特性,还能够用你的某个项目来测试 Migration Assistant,以便你对这些改变有所了解。因为在 Xcode 8 beta 下一个版本及 Siwft 3 正式发布以前,你没法向 App Store 提交 App,那么我建议暂时先不要讲代码迁移到Swift 3。算法
升级到 Swift 3编程
升级到Swift 3时,你会发现基本上每一个文件都须要改动!之因此这样,是由于全部的 Cocoa API 名称都改变了。简而言之,API仍是原来的 API,但这个 API 在 Objective-C 中是一种叫法,而在 Swift 中是另外一种叫法。Swift 3的语法书写起来要更贴近于天然语言。swift
在Xcode 8中,苹果提供了Migration Assistant,它能够完成大部分的迁移工做。固然,仍然有一部分工做是须要你手动完成的。api
你能够当即将代码升级到 2.3 或者 3.0。若是你须要将代码又转回来,你能够用 Xcode 的 Edit > Convert > To Current Swift Syntax… 菜单。编译器会和 Migrateion Assistant 同样智能。若是你在调用方法时,偶然使用了老的 API,编译器会显示一个 Fixt-It 选项,让你使用正确的新 API。幸亏 Swift 3 在最终发布时,才会中止改变源代码。所以,你能够将你的 Swift 代码保存为不一样的版本。可是 Swift 核心团队不能保证这一点之后不会改变,若是在某个时候不在保证源码上的兼容,他们会提供一个较长的过渡期。这意味着源码是稳定的,这样能鼓励更多的保守的公司去使用它。
这也说明,二进制稳定的目标尚未达到。本文最后将讨论这将致使的影响。
已实现的 Swift Evolution 提议
自从 Swift 开源以来,社区成员已经提交了超过 100 个改进建议。其中大部分(70多个)提议通过讨论修改以后已经被接受。还有一些仍然在激烈的争论以后被拒绝了。但最终,全部提议都要通过核心团队的最终拍板。
核心团队和广大社区之间的合做是卓有成效的。事实上,Swift 在 Github 上得到了 30,000 多颗星。每周都会有新的提议提交,周周如此。甚至苹果的工程师也会在 Github 上提交他们的提议。
在下一节中,你会看到一些相似 [SE-0001] 这样的标注。这是已经接受的提议编号,而且将在最终版的 Swift 3.0 中进行实现。每项提议都会标出,以便你查看每项改变的具体细节。
API 的改变
Swift 3 中最大的改变是标准库中在每一个库中都采用了统一命名方式。API Design Guidleines中包含了这些规则,核心团队在构建 Swift 3 时采用了这些规则,对新手来讲,这高度加强了可读性和易用性。核心团队遵循的是"好的 API 设计应当老是从调用者的角度看待问题"的原则。他们努力让 API 简单易用。再也不多说,让咱们开始介绍这些对你来讲很是重要的改变。
第一个参数的 label
咱们以一个开发者天天都会在 Swift 中用到的例子开始。
在函数或方法中的第一个参数如今必须有一个 label ,除非你显式地声明不要。之前,咱们调用一个函数或方法时,能够忽略第一个参数的 label[SE-0046]:
// old way, Swift 2, followed by new way, Swift 3 "RW".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding) "RW".write(toFile: "filename", atomically: true, encoding: NSUTF8StringEncoding) SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10) SKAction.rotate(byAngle: CGFloat(M_PI_2), duration: 10) UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline) UIFont.preferredFont(forTextStyle: UIFontTextStyleSubheadline) override func numberOfSectionsInTableView(tableView: UITableView) -> Int override func numberOfSections(in tableView: UITableView) -> Int func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? func viewForZooming(in scrollView: UIScrollView) -> UIView? NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true) NSTimer.scheduledTimer(timeInterval: 0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)
注意,有些方法使用介词"of"、"to"、"with"、"in"做为外部参数名。这是为了增长代码的可读性。
若是这个方法不使用介词也不使用 label,你应该在方法定义时,显式地在第一个参数名以前加一个下划线:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... } override func didMoveToView(_ view: SKView) { ... }
在许多编程语言中,许多方法能够共用一个方法名,但参数名不一样。Swift 也不例外,如今,你能够重载方法,APIs 可以将直接将它们转换成合适的调用。下面是一个例子,展现了 index() 方法的两种重载形式:
let names = ["Anna", "Barbara"] if let annaIndex = names.index(of: "Anna") { print("Barbara's position: \(names.index(after: annaIndex))") }
方法名是同一个,但参数名不一样,这将让人更容易记忆。
省略没必要要的单词
在早期的苹果标准库中,方法名中会包含一个单词,用于代表方法的返回值。由于 Swift 编译支持类型推断,这种作法其实并没必要要。核心团队尽量过滤一切"噪音",所以将这些重复的单词都删除了,只留下方法名中最重要的部分。
在将 Objective-C 库转换成本地 Swift 语言方面,API 变得更智能了[SE-0005]:
// old way, Swift 2, followed by new way, Swift 3 let blue = UIColor.blueColor() let blue = UIColor.blue() let min = numbers.minElement() let min = numbers.min() attributedString.appendAttributedString(anotherString) attributedString.append(anotherString) names.insert("Jane", atIndex: 0) names.insert("Jane", at: 0) UIDevice.currentDevice() UIDevice.current()
新的 GCD 和 Core Graphics
一提到遗留下来的“元老级”API,GCG 和 Core Graphics 更须要被从新“装扮一新”。
Grand Central Dispatch 经常使用于长时间计算或者与服务器通信。将任务放到不一样的线程,你能够避免阻塞用户界面。libdispatch 库是用 C 语言编写的,提供了 C 风格的 API。这个 API 如今在 Swift 中被从新设计为[SE-0088]:
// old way, Swift 2, followed by new way, Swift 3 let blue = UIColor.blueColor() let blue = UIColor.blue() let min = numbers.minElement() let min = numbers.min() attributedString.appendAttributedString(anotherString) attributedString.append(anotherString) names.insert("Jane", atIndex: 0) names.insert("Jane", at: 0) UIDevice.currentDevice() UIDevice.current()
相似的状况还有 Core Graphics。Core Graphics 是用 C 编写的,曾经以来一直只能以"丑陋"的函数方式调用。这是它的新的用法[SE-0044]:
// old way, Swift 2 let ctx = UIGraphicsGetCurrentContext() let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512) CGContextSetFillColorWithColor(ctx, UIColor.blueColor().CGColor) CGContextSetStrokeColorWithColor(ctx, UIColor.whiteColor().CGColor) CGContextSetLineWidth(ctx, 10) CGContextAddRect(ctx, rectangle) CGContextDrawPath(ctx, .FillStroke) UIGraphicsEndImageContext() // new way, Swift 3 if let ctx = UIGraphicsGetCurrentContext() { let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512) ctx.setFillColor(UIColor.blue().cgColor) ctx.setStrokeColor(UIColor.white().cgColor) ctx.setLineWidth(10) ctx.addRect(rectangle) ctx.drawPath(using: .fillStroke) UIGraphicsEndImageContext() }
枚举中 case 值的大小写
另外一个和过去的 Swift 代码不一样的地方是,在枚举中定义的 case 值如今使用小驼峰命名法。这是为了和属性名或者变量名保持一致[SE-0006]:
//old way, Swift 2, followed by new way, Swift 3 UIInterfaceOrientationMask.Landscape UIInterfaceOrientationMask.landscape NSTextAlignment.Right NSTextAlignment.right SKBlendMode.Multiply SKBlendMode.multiply
UpperCamelCase命名法如今只在类型名和协议名上使用。当你习惯这一切以后,Swift团队对于追求一致性的努力才没有白费。
返回值的方法或者修改值的方法
标准库中对方法名中使用动词和名词的规定也更加统一。你应当根据这个方法会致使什么后果或者要采起一些动做来进行方法命名。首要原则是若是这个方法名中包含"ed"或"ing"后缀,则代表这是一个名词。方法名为名词的方法有返回值。若是不包含这些后缀,则极可能这是一个动词。以动词命名的方法会对某块引用的内存进行一些操做。即所谓的"修改某个值"。下面是几个符合名词/动词命名规则的方法[SE-0006]:
customArray.enumerate() customArray.enumerated() customArray.reverse() customArray.reversed() customArray.sort() // changed from .sortInPlace() customArray.sorted()
下面是一些使用这些方法的代码片断:
var ages = [21, 10, 2] // 变量,不是常量,这样你才能修改它 ages.sort() // 修改值,如今值变成了 [2, 10, 21] for (index, age) in ages.enumerated() { // "-ed" 是名词,表示会返回一个 ages 拷贝 print("\(index). \(age)") // 打印:1. 2 \n 2. 10 \n 3. 21 }
函数类型
函数在声明和调用时,都须要用括号将参数括住:
func f(a: Int) { ... } f(5)
可是,当你用函数类型做为参数时,你可能会写出这样的代码:
func g(a: Int -> Int) -> Int -> Int { ... } // old way, Swift 2
你会发现代码很难读懂。参数在哪里结束,返回值从哪里开始?在 Swift 3 中,正确的定义方法是[SE-0066]:
func g(a: Int -> Int) -> Int -> Int { ... } // old way, Swift 2
如今,参数列表被括号包裹,而后才是返回类型。事情变得简单,同时函数类型更容易被识别出来。经过下面的比照,你会更清楚:
// old way, Swift 2 Int -> Float String -> Int T -> U Int -> Float -> String // new way, Swift 3 (Int) -> Float (String) -> Int (T) -> U (Int) -> (Float) -> String
API中新增功能
除了对已有 API 进行“旧瓶新装”这个最大的改变之外--有很是多的 Swift 社区正致力于此,也有对 Swift API 的一些功能上的增长。
访问所属类型
当你定义一个静态属性或方法时,你直接经过类或类型来调用它们:
CustomStruct.staticMethod()
若是你当前正在编写类型内部的代码,仍是要使用类型名来调用静态方法。为了使表述更加清晰,如今你能够经过 Self 来引用当前实例所属类型。S 为大写的 Self 表示引用当前实例的类型,而 s 为小写的 self 则引用当前实例自身。
这是具体的例子[SE-0068]:
struct CustomStruct { static func staticMethod() { ... } func instanceMethod() { Self.staticMethod() // in the body of the type } } let customStruct = CustomStruct() customStruct.Self.staticMethod() // on an instance of the type
行内Sequences
sequence(first:next:) 以及 sequence(state:next:) 是全局函数,返回一个无限序列。你传给它们一个初值或一个可变的状态,它们会在稍后调用闭包中的代码[SE-0094]:
for view in sequence(first: someView, next: { 0.superview }) { // someView, someView.superview, someView.superview.superview, ... }
还能够用 prefix 操做符来为序列加一个限制[SE-0045]:
for x in sequence(first: 0.1, next: { 0 * 2 }).prefix(while: { 0 < 4 }) { // 0.1, 0.2, 0.4, 0.8, 1.6, 3.2 }
杂项
keyPath() 等同于 #selector() ,帮助你减小输入错误
你能够在某些类型上调用 pi,好比:Float.pi、CGFloat.pi。大部分时候编译器可以推断出类型:let circumference = 2 * .pi * radius [SE-0067]
NS 前缀从老的 Foundation 类型中移除,如今能够用 Calendar、Date来替代 NSCalendar、NSDate 了
工具的改善
Swift 只是一门语言,大部分时候你都没法离开书写它的开发环境--对于苹果开发者来讲,也就是 Xcode!工具上的改变影响着天天编写代码的方式。
Swift 3 修正了编译器和 IDE 中的 Bug,还改善了报告错误和信息的精确性。就像你所指望的,每一个版本的发布都会让 Swift 和编译器的运行变得更快:
改善了字符串的 Hash 算法,致使在将字符串存入字典后,性能有 3 倍的提高
将对象从堆移到栈中存放,致使性能有 24 倍的提高(某些状况下)
编译器能够一次缓存多个文件(在整个模块被优化过的状况下)
优化了代码的大小,致使 Swift 代码的编译尺寸更小。以苹果的 Demobots 为例,编译尺寸缩减了原大小的 77%。
Xcode 也会更加智能地理解 Swift 代码:
过去,当你在一个 API 方法好比 sort() 上右击并跳转到定义时,你会看到一个不太容易理解的头文件。如今,在 Xcode 8 中,你会看到 sort() 方法其实是 Array 类的扩展。
Swift Snapshots就现在晚发布的 Swift Evolution 所说。在彻底合并到 Xcode 以前,Snapshots 提供了一个使用新语法的机会。Xcode 8 可以在 playgournd 中加载和运行 Swift Snapshots。
Swift 包管理器
开源后的 Swift 实际上包含了 Swift 语言自己、核心库和包管理器。这三者一块儿构成了咱们所见到的 Swift。Swift 包管理器定义了一个简单的目录结构,其中包括了你所共享和导入到项目中的 Swift 代码。
相似于你所用过 Cocoapods 或 Carthage 这些包管理器,Swift 包管理器会下载依赖项并编译它们,把它们 link 成库或可执行文件。已经有 1000 个库支持 Swift 包管理器,而且在将来几个月中,还会有更多的库支持它。
计划中的特性
前面说过,Swift 3 尽可能避免不兼容的改变,从而使你的代码可以从一个版本与下一个版本兼容。若是这是真的,那么在此次发布中应该还有一些更远大的目标未能达到,即泛型加强(generic additions)和 ABI(程序二进制接口)稳定。
泛型加强包括递归协议约束和可以将一个受约束的扩展符合新协议(例如,一个用于存放 Equatable 对象的数组也应当遵照 Equatable 协议)。在这些特性还没有实现以前,Swift 不能被视做 ABI 稳定的。
ABI 稳定要求应用程序和库不管使用哪一个版本的编译器编译,都能被 link 和可以彼此进行交互。这对于第三方库来讲是个重要的进步,由于这样就能够不须要提供源码就能够封装出框架了,而如今新版本的 Swift 不但要这些第三方库修改代码,还要从新进行编译。
此外,若是 ABI 稳定就能够移除包含在二进制文件中的 Swift 标准库,由于就目前来讲 iOS 和 macOS APP都是用 Xcode 建立的。目前的二进制文件中包含有 2 MB 左右的文件大小是为了确保可以兼容将来的操做系统。
总之,你如今只能保持源代码的版本兼容,而二进制版本的兼容当前仍然是不可能的。
结尾
Swift 仍然在不断演进,由于社区的目标是最佳实践。虽然它还很年轻,但它同时也面临着无比巨大的机遇和美好将来。Swift 如今已经可以在 Linux 上运行了,在将来几年,你还会看见它在更多的服务器和设备上运行。从头设计一门语言必然会破坏 ABI 稳定性,一旦等它稳定后这种破坏就变得小多了。这(开源)是惟一可以让这门语言变得更好的机会,不然咱们必将后悔莫及。
Swift 正在扩大它的影响力。苹果正在身体力行。苹果的团队们正在 Music app、Console、Sierra画中画、Xcode 文档查看器、新的 iPad 版的Swift Playground app 中使用 Swift。
说到这里,苹果好像有一种让非程序员学习 Swift 意图,例如能够在 iPad 上使用 Swift 以及某些教育措施都代表了这一点。
另外,Swift 正在持续改进:命名变得更规范,代码读起来更清晰,你能够用工具来迁移代码。若是你想进一步深刻了解,请看WWDC 的会议视频。
能够确定的是,2016年底 Swift 3 的正式发布必将使人期待。咱们将持续跟进全部改变,请继续关注咱们的教程、新书预告和视频,咱们将开始使用这些使人兴奋的改进。