原贴:What's New in Swift?
做者:Ben Morrow
译者: kemchenjhtml
Swift 3.0年底就会发布正式版, 而且会给全部Swift开发者带来不少改变.ios
若是你没有一直紧跟Swift Evolution的话, 你也许会想知道都有哪些改变, 以及它将会怎么影响你的代码, 而且你该何时开始着手把代码convert到3.0, 那这篇文章就是写给你的.git
这篇文章里, 我将会着笔于那些对你的代码会有重大影响的Swift 3的新特性. 开始吧!程序员
Swift 3.0预览版在Xcode 8.0 beta已经可使用了. 当Swift 3.0的改革已经接近尾声的时候, 接下来的几个月也许还会再接受几个提案, 功能性修改将会在2016年年底xcode发布正式版以后中止, 因此你必须等到那个时候才能把你的Swift 3应用提交到App Storegithub
为了让开发者可以开始着手移植到Swift 3.0, Apple已经把Swift 2.3内置到Xcode 8.0里. 对开发者来讲, Swift 2.3跟2.2同样, 但2.3支持WWDC上宣布的那些新的SDK和Xcode的新功能, Xcode 8.0出来以后, 你就能够提交使用Swift 2.3写的app, 而不用等到3.0才能体验到那些新的特性.objective-c
我建议尝试一下咱们在playground实现的功能, 甚至能够在你的某个项目里跑一下Migration Assistant感觉一下那些改变. 但Xcode 8.0和Swift 3.0还没出正式版以前, 你还不能够提交app到App sSore里, 你也许须要考虑一下等Swift 3安定下来以后再把本身的全部代码移植过去.编程
转到Swift 3.0的时候, 你必须意识到全部的文件都须要作修改! 这么大的改变是由于Cocoa的API名字都改了. 或者为了更加明确, API会保持原样, 但会有一个合适的名字给Objective-C和另外一个给Swift. 接下里的几年里, Swift 3.0将会让Swift变得更加Natrual to writeapi
Apple在Xcode 8.0里内置了可以帮你智能地完成大部分迁移工做的Migration Assistant. 但你仍是须要点几个按钮去完成这个过程.xcode
你马上能迁移到Swift 2.3或者Swift 3.0, 若是你须要迁移回去2.2的话, 你随时能够到Xcode的菜单栏Edit -> Convert -> To Current Swift Syntax…迁移回去. 编译器也能像Migration Assistant同样智能, 若是你使用了旧的API的话, 编译器会提供一个Fix-I他的选项来帮助你改为正确的API.浏览器
这里面最好的消息是, Swift 3.0的目标是做为最后一个须要大量修改代码的版本存在(API将会稳定下来). 往前看的话, 你将可以Swift的版本升级过程当中保持你的代码不变. 固然, Swift的核心团队没办法预知将来, 但他们承诺即便之后须要修改代码以完成版本升级的时候, 也会保持长时间的向后兼容. 语言的稳定意味着愈来愈多保守的公司也采用Swift.
但二进制接口稳定下来的目标仍是没能达成, 文章末尾你能看到这会带来的影响
自从Swift宣布开源以后, 社区成员已经提交了超过100个Proposal了. 大部分提案 (70个以上 )都在讨论和修改以后被接受了. 那些被驳回的提案也都通过了激烈的讨论. 最后, 由核心团队来对全部提案作最后决定.
核心团队跟社区的合做让人印象深入. 实际上, Swift已经在Github上有了三万个star了. 每周都会有几个新的提案提交上去. 甚至连Apple本身的工程师在想要作出改变的时候也会打开Repo去写提案.
在接下里的章节里, 你会看到相似于[SE-0001]这样子的超连接. 这些是Swift Evolution的提案编号. 这些提案都已经被接受而且将会在Swift 3.0的最终版本里实现. 每一个提案的连接点进去你都能看到关于每一个修改的所有细节.
Swift 3.0最大的升级是标准库将会沿用以前的命名规范, API Design Guidelines包括了团队在完成Swift 3.0的时候定下来的规范, 对于新程序员有更高的可读性和易用性. 核心团队坚守”Good API Design always consider the call site”. 他们力争明确每个使用的场景. 如下是一些立刻会影响你的改变.
让咱们先来颠覆一个你天天都在Swift里的操做吧
函数和方法的第一个参数名不会再自动省略了, 除非你用了”_”. 以前你每次调用一个方法或者函数的时候都会自动省略第一个参数的参数名.
// 第一个是 Swift 2的写法 // 第二个是 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
这样的介词做为外部参数名(而再也不放在函数或者方法名里). 这一部分的修改是为了提升可读性.
若是调用方法的时候不须要介词和外部参数名的时候, 你应该在第一个参数名前面加一个下划线_来代表:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... } override func didMoveToView(_ view: SKView) { ... }
在不少编程语言里, 不一样的方法也许会用同一个名字但使用不一样的参数名. Swift也不例外, 在API修改得更加直接以后, 如今咱们能够经过重载来使用同一个方法名调用不一样的方法. 下面是index()的两种调用方式:
let names = ["Anna", "Barbara"] if let annaIndex = names.index(of: "Anna") { print("Barbara's position: \(names.index(after: annaIndex))") }
如今, 变量名的改变让方法名可以更加具备一致性, 而且更容易理解
在以前Apple的库里面, 方法名一般会包含一个名字去代表返回值的类型, 但如今得益于Swift编译器的类型推断能力, 这件事情变得再也不那么必要了. Swift核心团队花了不少功夫去过滤杂音, 提取提案里的真正需求, 就这样不少重复的词都去除掉了.
Objective-C和C语言库的API变得更加接近原生Swift [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()
提到顽固的老API, GCD和Core Graphics的语法都获得了美化
GCD被用来处理诸如耗时运算和服务器通讯的多线程任务, 经过把任务移到别的线程, 你能够防止本身的用户界面线程被阻塞. libdispatch这个库是用C语言写的, 而且API也是C语言风格的. 这一套API如今被从新塑形成原生的Swift风格 [SE-0088]:
// Swift 2的写法 let queue = dispatch_queue_create("com.test.myqueue", nil) dispatch_async(queue) { print("Hello World") } // Swift 3的写法 let queue = DispatchQueue(label: "com.test.myqueue") queue.asynchronously { print("Hello World") }
一样的, Core Graphics也是C语言写的, 而且之前都是使用很怪异的方法去调用. 新的用法就像这样 [SE-0044]:
// 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() // 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() }
另外一个彻底不一样的地方来自于你平常编写Swift代码的方式, 枚举的值如今要用首字母小写的驼峰命名法(lowerCamelCase). 这会让他们看起来跟其余Property和Values更加一致 (向Struct和Class靠拢) [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)如今只有类型(Types)和协议(Protocols). 你也许须要一些时间去适应, 这是Swift团队对于一致性的执着.
标准库里方法名的动词和名词都更加具备一致性, 你能够根据action形成的效应去选择命名方式(You choose a name based on side effects or the action taken). 最重要的原则是若是方法名包含了-ed
或者-ing
这样的后缀的话, 那么就能够把方法当作一个名词, 一个”名词方法”将会返回一个值, 若是它不包含后缀, 那很像是一个动词, 这些”动词方法”将会在引用对象的内存里进行操做, 也就是修改对象. 这里有几对动词和名词方法遵循这样的原则 [SE-0006]:
customArray.enumerate() customArray.enumerated() customArray.reverse() customArray.reversed() customArray.sort() // 原来的写法 .sortInPlace() customArray.sorted()
这里是一些他们的使用时的片断:
var ages = [21, 10, 2] // 这里是变量而不是常量, 因此能够修改 ages.sort() // 在这里进行修改, 当前值为 [2, 10, 21] for (index, age) in ages.enumerated() { // "-ed" 后缀意味着会返回一个修改后的副本 print("\(index). \(age)") // 1. 2 \n 2. 10 \n 3. 21 }
函数声明和调用老是会要求用圆括号()把参数包起来:
func f(a: Int) { ... } f(5)
不过, 当你使用函数做为参数传入的时候, 你也许会写成这样:
func g(a: Int -> Int) -> Int -> Int { ... } // Swift 2的写法
你也许会发现这样可读性不好, 参数在哪里结束? 返回值在哪里开始? Swift 3.0里定义函数的正确方式是这样的 [SE-0066]:
func g(a: (Int) -> Int) -> (Int) -> Int { ... } // Swift 3的写法
如今参数必须被圆括号()包住, 后面跟着返回值类型. 一切都变得很清晰, 具备连贯性, 函数的类型更容易看出来. 下面一个很明显的对比:
// Swift 2的写法 Int -> Float String -> Int T -> U Int -> Float -> String // Swift 3的写法 (Int) -> Float (String) -> Int (T) -> U (Int) -> (Float) -> String
Swift 3.0最大的更新是现有API变得更加现代化, 有愈来愈多的Swift社区在致力于这件事, 包括一些额外的, 实用API
当你定义一个static
的property或者method的的时候, 你必须经过他们的Type去调用他们:
CustomStruct.staticMethod()
若是你是在一个type里写代码, 你仍是须要在type里经过type名去调用static method. 为了让代码更加清晰, 你能够用Self
去替代当前type的, S大写的话Self
表明type自己, s小写的话self
表明实例对象(instance)自己.
这里是它的具体例子 [SE-0068]:
struct CustomStruct { static func staticMethod() { ... } func instanceMethod() Self.staticMethod() // 在结构体CustomStruct内部使用Self指向CustomStruct这个结构体自己 } } let customStruct = CustomStruct() customStruct.Self.staticMethod() // 在结构体实例(instance)里的调用
sequence(first: next:)
和sequence(state: next:)
是返回无限的sequence的全局方法. 你能够给他们一个初始值或者一个可变的状态, 而后他们会经过闭包进行懒加载 [SE-0094]
for view in sequence(first: someView, next: { $0.superview }) { // someView, someView.superview, someView.superview.superview, ... }
你能够经过prefix
关键字来给sequence增长限制 [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()
的工做方式很像, 可以帮助你纠正那些字符串类型的API
你能够在你想要使用的类型里调用pi
, 就像这样: Float.pi
, CGFloat.pi
. 大部分时候编译器还能够帮你推导出类型: let circumference = 2 * .pi * radius
[SE-0067]
在那些老的Foundation里NS
开头的类已经去掉了, 如今你能够直接使用Calendar
, Date
而不是NSCalendar
和NSDate
.
Swift是一门语言, 编写代码的大部分工做须要开发环境, 对于Apple的开发者来讲就是Xcode! 这些工具改变将会影响你天天编写Swift代码的方式.
Swift 3修复了编译器和IDE的bug. 也提升了error和warning信息的可读性. 就像你所预想的, 每一次更新, Swift在运行和编译的时候都会变得更快:
字典里字符串的hashing速度是之前的三倍
把对象从栈移到堆的速度是之前的24倍(某些状况下)
编译器能够一次性缓存多个文件(在整个模块优化的时候)
代码大小优化能够减小Swift代码编译后的大小. Apple的demo Demobots把可以把渲染后的大小变为原来的77%
Xcode也学会了如何去解构原生Swift:
当你右键点击某个API的方法, 例如sort()
的时候就会跳到它的定义, 之前你会被带到一个晦涩难懂的头文件里. 但如今, Xcode 8, 如你预想的, 你会看到的sort()
会是Array
的一个拓展
Swift Snapshots会像Swift Evolution的Nightly Builds(天天编译一次的最新版本). 它提供了一个机会去在彻底加入Xcode以前测试那些新的语法, Xcode 8能够在playground里加载和运行Swift Snapshots
开源的Swift实际上包括了语言自己, 核心库, 和包管理器这些repo. 这些套件的组合构成了咱们认识的那个Swift. Swift Package Manager定义了一个简单的路径结构, 可以把代码让你分享和导入项目里
相似于你可能用过的Cocoapods或者Carthage同样的包管理器, Swift的包管理器能够下载和编译依赖, 而且自动把他们连接到一块儿去建立库或者可执行程序. Swift 3是第一个包含包管理器的版本, 已经有超过1000个库支持这个功能, 而且在以后的几个月里, 你会看到它的更多种形式
就像以前所说的, Swift 3的目标是让你可以保持你的代码可以在之后版本里稳定下来, 避免将来会有破坏性的改变. 虽然如此, 但仍是有些相关的重要目标在这个版本里没有完成, 泛化和ABI兼容.
泛化(Generics)还有递归协议约束, 以及新协议对于拓展(Extesion)的约束.(例如: 两个具备相同元素的Array是没法判断是否相等的Euqaltble). 在这部分工做完成以前, Swift是不能完成ABI兼容的.
ABI兼容可让不一样版本编译出来的程序和库可以连接到一块儿而且进行交互. 对于第三方库来讲这是很重要的, 它们能够直接把本身的库分享出去而没必要连同代码也一并提供, Swift的升级的时候, 这些第三方库也没必要升级代码以及从新build框架.
另外, 二进制接口的稳定可让程序再也不须要连同标准库一块儿打包, 就像如今发生在Xcode编译出来的iOS和macOS程序同样, 如今这些二进制可执行程序全都包括了2MB左右的Swift标准库以保证他们可以在之后的操做系统里也能运行.
总结起来, 你如今可让你的代码在版本升级的时候保持稳定, 没必要作修改, 但编译出来的二进制可执行程序则可能仍是会出现不兼容的情况.
在社区的努力下Swift还在一直进化中. 如今仍是Swift的萌芽期, 这门语言拥有无限的潜能和美好的将来. Swift已经能够在Linux上运行, 再过几年咱们也许极可能就能够看到它在服务器上跑起来. 设计一门语言, 对于语言的修改确定会有它的好处和破坏接口稳定的可能性, 但当它稳定下来以后, 你会发现一切都是值得的, 这是一次可贵的机会可让这门语言变得更好.
Swift的影响力也慢慢地在扩大, Apple is eating their own dogfood (Angular的梗, 讽刺Google不用本身家出的Angular JS), 苹果的团队已经在Music, Console, Sierra的画中画, Xcode的文档浏览器和新的Swift Playground的iPad版本上用了Swift了.
对于非程序员人群在iPad上学习Swift和通识教育有极大的推进做用.
简单来讲, Swift还在持续上升期: 命名变得更好, 代码更清晰, 而且你也有工具可以帮助完成迁移, 若是你还想了解更多, 能够看WWDC的视频