Swift 3 新特性

本篇文章翻译自:What’s New in Swift 3?
原作者:Ben Morrow on June 15, 2016


前言

Swift 3今年晚些时候会与大家见面,它会带给Swift开发者巨大的代码层面的改变。
如果你最近没有跟进Swift Evolution的步伐,你也许会问到底有啥变化,它会怎样影响你的代码,以及你什么时候应该迁移到Swift 3上来。那么这篇文章会为你一一解答。
在这篇文章中,我会着重强调Swift 3中最重要的变化,因为这些会影响你的代码。我们一起来深掘吧!

开始

现在Swift 3预览版已经可以在Xcode 8 beta版使用了。尽管Swift 3的进化之路就要接近尾声,但是在接下来的几个月还是会做出一些变化。Swift 3的特性最终确定下来是在2016年,Xcode 8 正式版本发布的时候,所以你不得不推迟采用Swift 3编写的 App的发布日期。

为了让开发者能够按照他们自己的方式迁移到Swift 3上来,作为一个小的升级包,Xcode 8会包含Swift 2.3。从开发者的角度看,Swift 2.3跟Swift 2.2是一样的,只不过它会支持更多新的SDKs和WWDC上宣布的Xcode新特性。一旦Xcode 8 发布正式版,且你还没有迁移到Swift 3上来,那么你可以提交用Swift 2.3编写的App。

我建议你在playground中测试我们要讨论的特性,还有你也许可以在你的一个工程里运行迁移助手(还是不要拿公司项目做小白鼠了⚒),来感受一下新技术带来的变化。因为直到Xcode 8 正式版发布,Swift 3最终确定下来,你才能发布app,所以你该考虑等到所有事情有了定论再迁移到Swift 3上来。

迁移到Swift 3

当转换到Swift 3,你会发现几乎所有文件都需要改变!这主要是因为Cocoa API命名发生了改变。或者更准确的说,API相同的,但是现在OC,Swift的命名不统一。Swift 想要在接下来的时间让Swift的书写更加自然。

苹果在Xcode 8 中集成了迁移助手,它可以智能地帮助你修正这些变化。但是不要惊讶,如果你想要自行修改一下地方,那么迁移助手是不会自动帮你处理的。

你可以很快地转换到Swift 2.3 或者 Swift 3上来。如果你想转换回来,你可以在Xcode这么操作:Edit > Convert > To Current Swift Syntax ... 。编译器像迁移助手一样智能,如果你在一个方法的调用上使用了旧的API,编译器会提供一个可选选项帮助你修正,这样你就能使用正确的现代的API了。

最重要的消息是:Swift 3目标是成为做出重大变化的最后一个版本。所以当Swift从一个版本迭代到另一个版本时,你能够保持自己的代码不变。尽管Swift 核心团队不能预测未来,但是他们承诺如果真的需要破坏源兼容性,他们会提供一个相当长的废弃周期。那就意味着Swift语言具有了源稳定性,这会促进更多保守公司采用Swift语言。

据说,二进制稳定性的目标还没有实现。在文章结尾,你会了解更多二进制稳定性的影响。

已经实现的---Swift进化提议

自从Swift开源,社区成员对于Swift的改变,给出了上百条提议。大多数提议(目前70条,或者更多)在讨论后都被接受了。那些被拒的提议也是在经过激烈的讨论后,做出的决定。无论如何,最终Swift核心团队会对所有的提议做出决定。

Swift核心团队与社区展开了广泛的互动交流。事实上,Swift在Github上获得了3万多颗星。日复一日,每周都有好几条提议被提出。甚至苹果的工程师想要做出改变时,也会在Github开出新的仓库,给出提议。

在以下部分,你会看到诸如[SE-0001]的标签。这些是是Swift Evolution 提议编号。这里的有编号的提议都是已被接受,会在Swift 3中实现。同时也会提供每一个提议的链接,你可以查看每一个改变的具体细节。

API 变化

Swift 3最大的更新是标准库采用了统一的命名规范。API 设计指南包含了团队构建Swift 3遵守的规范,这对于编写可读性,易维护性代码具有很重的价值。核心团队实践这样的准则:好的API设计总是考虑到调用场景。他们努力让API的用途变得更加清晰。废话不多说了,接下来是最有可能影响到你的变化。

第一个参数标签

我们以一个我们每天都在使用的实例开始。


函数和方法中的第一个参数会需要一个便签,除非你有其他方面的需求。之前,当你调用一个函数或者方法时,你是省略第一个参数标签的[SE-0046]:

//第一条为Swift 2中旧的方式, 下一条是Swift 3中新的方式
"RW".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)

"RW".write(ToFile:"filename", atomically: true, encoding: String.Encoding.uft8)

SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
SKAction.rotate(byAngle: .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)
Timer.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))")
}

总之,参数名的变化让方法名更加统一,也更加容易学习。

省略不必要的单词

在之前的苹果库的版本迭代中,方法会包括一个名字,用来指示返回值。得益于Swift编译器的类型检查,这显得不是很必须。团队认真审视后过滤掉了这些噪音只留下信号,结果很多重复的单词都被移除了。

API在OC库转换到原生Swift方面更加智能 [SE-0005]:

//第一条为Swift 2中旧的方式, 下一条是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的反对,GCD和Core Graphics成了最需要被改造的两个。

GCD用在多线程任务中,例如:耗时的计算工作或者与服务器的通信。通过切换任务到不同的线程,可以避免阻塞用户线程。libdispatch库是用C语言写的,而且采用C语言风格的API。现在这些API被以原生Swift的风格重新设计。

// 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.async {
    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 新方法
guard let ctx = UIGraphicsGetCurrentContext() else {
    return
}
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()

枚举 Cases 首字母

另一个与你已经习惯的Swift编码方式不同是,枚举case 现在使用lowerCamelCase。这会让他们与其他属性更加一致 - [SE-0006]:

//第一条为Swift 2中旧的方式, 下一条是Swift 3中新的方式
UIInterfaceOrientationMask.Landscape
UIInterfaceOrientationMask.landscape

NSTextAlignment.Right
NSTextAlignment.right

SKBlendMode.Multiply
SKBlendMode.multiply

UpperCamelCase现在只在类型名和协议名上有保留。这可能要花上一段时间来适应,但是Swift团队这么做有他的理由---他们在努力达成一致性。

有返回值的函数和修改自身的函数

Swift标准库在用名词,动词的函数命名会越来越统一。你依据动作发生带来的影响来命名一个函数。经验法则是:如果它包含一个像"-ed" 或者"-ing"的后缀,那么认为这是一个名词函数。名词函数会返回一个值。如果它没有后缀,那么它很可能是一个必要的动词。这些"动词"函数在引用内存执行动作。这也被称为modifying in place。以下是遵守名词/动词规则的函数对。这里还有一些函数对[SE-0006]:

customArray.enumerate()
customArray.enumerated()

customArray.reverse()
customArray.reversed()

customArray.sort() // changed from .sortInPlace()
customArray.sorted()

这里是一些行为上差异:

var ages = [21, 10, 2] //变量,可以修改
ages.sort()  //modify in place(这个翻成就地修改?), 值现在是[2, 10, 21]

for (index, age) in ages.enumerated() { //"-ed" 名词 返回一个copy。
    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中正确的定义这个函数是 [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

新增API

尽管Swift 3最大的更新是对现存API的修改,但是Swift社区也在添加新的API方面,做了大量的工作。

获取包含类型(或者说是隐含类型)

当你定义一个静态属性或者方法时,你总是通过类型直接调用他们:

CustomStruct.StaticMethod()

如果你在一个类型的中编写代码,你还需要一个类型名来调用静态方法。为了更加清晰,现在你可以调用Self来获取隐含类型。大写字母"S"获得self的类型,小写字母"s"获取self的实例。

这里会解释工作原理[SE-0068]:

struct CustomStruct {
    static func staticMethod() {}
    
    func instanceMethod() {
        Self.staticMethod() //在类型体内部, 之前要写成CustomStruct.staticMethod()
    }
}

let customStruct = CustomStruct()
customStruct.Self.staticMethod()

注:这个特性会被加到 Swift 3, 但是目前在Xcode 8 beta 5中还不能用。

内联序列

sequence(first: next:)和sequence(state: next:)是全局函数他们返回无限序列。你可以给他们一个初始值,或者初始状态,他们会以懒加载的方式应用到一个闭包[SE-0094]:

for view in sequence(first: someView, next: {$0.superView}) {
       //someView, someView.superView, someView.superView.superView, ...
}

你可以通过添加前缀操作符,来限制序列

for x in sequece(first: 0.1, next: {$0 * 2}).prefix(while:{$0 < 4}) {
       //0.1, 0.2,0.4,0.8,1.6,3.2
}

注:这个特性会被加到 Swift 3, 但是目前在Xcode 8 beta 5中还不能用。

其他零碎的变化

  • #keyPath()#selector()一样工作,这样可以避免在使用字符串类型的APIs,打错字。

  • 你现在可以通过你想要使用的类型调用pi, 例如: Float.pi, CGFloat.pi。大多数时候,编译器能够推断出类型:let circumreference = 2 * .pi * radius[SE-0067]

  • 旧函数类型的前缀NS被移除。你现在可以使用Calendar, Date 替代 NSCalendar和NSDate。

工具上的提升

Swift是一门语言,编写Swift代码要用到开发环境-苹果开发者就是Xcode了!工具的变化将会影响到你每天怎么编写代码。
Swift 3修复了编译器和集成开发环境中的bugs。也提高了错误和警告信息的精度。如你所料,每一次发布新版,Swift运行和编译速度都变得更快了。

  • 通过改善字符串哈希,字符串类型字典速度提升3倍。

  • 把对象的存储从堆转移到栈,速度提升了24倍(在某些情况下)

  • 编译器现在一次缓存多个文件(在整个模块的优化)

  • 代码体积优化降低了Swift代码编译后的体积。苹果的demo-Demobots编译后的体积降低到原来的77%。

Xcode正在学习以原生Swift的方式进行思考:

  • 当你右击API方法像sort(),会跳到方法的定义,你通常会被带到奇怪的头文件里。在Xcode 8中,如你所料它是Array的一个扩展方法。

Swift快照像是Swift Evolution的后续版本。在完全集成进Xcode前,他们提供一个机会来使用新的语法。Xcode 8可以在Playground中加载并运行Swift快照。

Swift包管理器

Swift源码实际上是几个仓库的集合,包括:语言,核心库,还有包管理器。此套件一起构成了我们所认识的Swift。 Swift包管理器为你的分享代码和工程代码定义了一个简单的目录结构。
和你所熟悉的Cocoapods或者Carthage相似,Swift包管理器会下载依赖,并编译,链接他们来创建库和可执行文件。Swift 3是支持Swift包管理器的第一个版本。目前,有1000多个库已经支持包管理器了,而且在接下来的几个月,会有更多的库加入进来。

预计未来的特性

之前提到过,Swift 3 通过避免重大变化,让你能够在Swift版本迭代中保持你的代码。但是事实上,一些崇高的目标还有没有达到,即泛型添加和ABI稳定。
泛型添加包括递归协议约束和让一个受约束的扩展遵守一个新的协议(例如:由遵守Equatable的元素组成的数组也遵守Equatable协议。)在这些特性完成前,Swift不能宣布ABI稳定。

ABI稳定允许不同版本的Swift应用,库一起编译,链接,和相互通信。ABI稳定对于第三方库不提供源码情况下组装框架至关重要,因为Swift新版本要求他们不仅要更新代码,还要重新构建框架。
此外,就目前Xcode创建的iOS和macOS应用来看, ABI稳定消除了组装Swift标准库和二进制文件的需求。现在二进制文件捆绑着2M额外大小的文件,这确保他们可以运行在未来的操作系统上。

所以总而言之,你可以在Swift版本迭代中保持你的源码,但是版本之间,编译后的二进制文件的兼容问题还没有解决。

总结

随着社区的不断实践,Swift持续发展。虽然还在起步阶段,但是这门语言有很大动力和明朗的未来。Swift已经运行在Linux上了,而且在未来几年内,你很有可能看到它运行在服务器端。从头设计一门语言有它的优势,一旦ABI稳定下来,想要再打破它的可能性会非常小。现在正是更正语言的好机会,绝不后悔。(哈,swift 3是多么被寄予厚望呀!😈)

Swift正在扩张。苹果也在使用他们自己的产品。苹果团队在音乐APP,控制台,Sierra画中画,Xcode文档查看器和ipad版的Swift Playgroud中使用了Swift。
说到这里,应该会有一个很大的推动力促使非程序人员通过iPad或者教育计划学习Swift。
这里的关键是Swift还在不断的上升:命名变得越爱越好,代码变得越来越清晰,还有工具帮助你做迁移。如果你深受启发,想要深入挖掘,你可以观看 WWDC session videos.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 作者:COSMIN PUPĂZĂ,原文链接,原文日期:2016/06/29译者:saitjr;校对:Cee;定稿:...
    梁杰_numbbbbb阅读 657评论 0 5
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,029评论 4 62
  • 《水浒传》,四大名著之一,讲述梁山好汉108将,“拳打镇关西”“智取生辰纲”等一段段脍炙人口的故事。 从小,便对梁...
    支策阅读 911评论 0 2
  • 我们常说“命运掌握在自己的手中”,可是面对生活困难的时候,却往往退缩,抱怨环境的恶劣,人情世故的不公,以及自己太弱...
    霞姐_02f1阅读 318评论 0 0
  • 逻辑梳理:1.判断openID 和 accessToken 是否存在微信是否登录,已登录,从后台获取个人信息并显示...
    lalala1112389阅读 1,196评论 0 1