Loading document…
Opening in Pages for Mac...
Your browser isn’t fully supported.
For the best Pages for iCloud experience, use a supported browser.
Learn More
Cancel
Continue
首先我们来看看
swift 4
中有哪些新东西?
1
、
One-sided ranges
2
、
Strings
3
、
Private declarations visible in same-fi le extensions
4
、
Key paths
5
、
Encoding and decoding
6
、
Dictionary and Set enhancements
7
、
MutableCollection.swapAt method
8
、
Generic subscripts
9
、
NSNumber bridging
10
、
Class and subtype existentials
接下来看看今天的主要内容有哪些:
Language refi nements and additions
Source compatibility
Tools and performance
Standard library
Exclusive access to memory
上面都是一些总结性的,下面会讲具体有写新东西。
“Private” Access Control
我们经常会写一些
“private”
的方法或者属性,比如这样:
这个结构体
Date
实现了
Equatable
和
Comparable
协议。这两个协议的两个方法里都用到了
Date
的私有属性。但是这样写的话代码看起来就有点乱了。所以我们会这样写:
这样看起来就清爽多了。但是在
Swift 3
并不能很好的支持这个。因为在
extension
里不能访问私有
属性。所以我们就要用
fi leprivate
来解决这个问题。然后在整个文件中我们都可以访问这个属性
了。这可能与我们真正的目的会有冲突,因为在整个文件中都可以访问这个属性了。在
Swift 4
中
对
private
的访问控制扩展成了可以在同一个源文件的扩展中进行访问了。
Composing Classes and Protocols
先来看一个例子:
我们定义了一个协议叫
Shakeable
,然后
UIButton
和
UIControl
同时都实现了这个协议。
这时我们有一个方法叫
shakeEm
,
这个时候问号那里要填入什么类型呢?如果是
UIControl
的话,并不是所有的
UIControl
都是
Shakeable
的,如果是
Shakeable
的话,后面
where
语句里的条件又无法满足。
Swift 4
引入了一个
新的概念来将类和协议组合起来,这就是
&
。
在
OC
里我们很容易就实现上面的功能
这样的定义在
OC
里很常见,在
Swift 3
里的话我们就没有办法这样定义了,我们只能用
Optionals
来实现了。
这其实就有点尴尬了,因为想的类型不只是
NSView
,而是
NSView
和
NSTextInputClient
。在
Swift
4
中就不会这样了,我们可以用
&
符号来解决这个问题。
接下来还讲了几个没有在这个
Session
里将的功能,比如
KeyPaths
、
Key-Value Coding
、
Archival
& Serialization
,这些都在
“What’s New in Foundation”
里有讲。
Source Compatibility
Swift 4
和
Swift 3
大部分都是兼容的。原因就是语言没有大概,而是做了很多的改良和加法。比如
访问控制和类和协议的组合就是改良和加法的例子。比如
Swift 2
到
Swift 3
和
Swift 1
到
Swift 2
的
变化来说,这一次没有大范围的改变。因此我们也能获得一个更平滑的迁移了。
Swift 3.2
是
Swift 4
编译器的一部分,不是一个单独编译器或者工具链。编译器允许在
Swift 4
中有
Swift 3
的语法。当某个
API
在
Swift 3
和
Swift 4
中不一样的时候,它会自动回滚到
Swift 3
的版本。我们
看到的结果就是用
Xocde 9
打开
Swift 3
的工程的时候,不需要做什么工作就可以正常的编译和运
行。大部分
Swift 3
的代码都不需要修改就可以正常编译。
Swift 3.2
已经提供了大部分
Swift 4
的功能和新的
SDK
了。
从
Swift 3.2
迁移到
Swift 4
之前
从
Swift 2
迁移到
Swift 3
的时候,如果我们用的是最新的
Xcode
,它会强制要求我们迁移到
Swift 3
,除非我们用旧版本的
Xcode
来编译
Swift 2
的项目,否则是无法用新的
Xcode
来编译
Swift 2
的项目的。但是现在呢,
Swift 3.2
可以和
Swift 4
共存了。也就是说,我们不一定要做迁移
也能用
Xcode 9
编译
Swift 3.2
的项目,而且还可以设定每个
target
的
Swift
语言版本了。
Swift Package Manager
Swift Package Manager
会选择合适的
Swift
语言版本。
Build improvements
Xcode 9
有一个全新的构建系统的实现,它是用
Swift
写的,并且是构建在开源的
LLBuild
引擎之上
的。它能够快速的计算不同构建步骤的依赖。在大工程的增量构建的时候你就会注意到它的优势
了。这是
Xocde 9
的一个新技术的预习。建议大家可以通过打开设置去试试。
Precompiled Bridging Headers
我们经常会在
Swift
的项目中使用
OC
的库或者一些类,这个时候我们就要用到
Bridging Headers
了。当这个
Header
是里的内容太多的时候,就会降低编译的速度了。
所以
Xcode 9
就有了预编译的
Bridging Headers
了,所以只需要编译一次。之前是需要针对每个
Swift
文件都要编译一次。这个改变是的
Apple Music
应用的编译速度提升了
40
%
。非常的厉害。
Shared Build for Coverage Testing
Xcode 8
的是单独为
Coverage Testing
进行构建的。
Coverage
工具其实是比较低开销的。但是在
Xcode 9
却可以共享同一个构建,可以避免构建两
次。如果在测试的时候开启了
Coverage
的话,就会在项目构建的时候将这个包含进来,当然,这会
带来一些开销,但是经过测试,带来的开销要少于
3
%
。
Indexing While Building
看到这个应该是很多人的痛了吧。虽然是在后台操作,但是也做了大量重复的工作。所以在构建的
时候会自动更新这个索引,就不需要再在后台更新索引了。这带来的开销特别小。
Predictable Performance
上面的图展示的是排序有
100
个元素的
100,000
个数组所耗费的时间。但是数组元素的大小是不一样
的。对于只有一个字的结构体来说,只需要不到
2s
的时间。当值的大小变成
2
个字的时候,时间增
加的很少。但是当增加到
3
个字的时候,时间也没有增加多少。但是当大小变成
4
个字的时候,时
间突然一下就增多了。想要了解是怎么回事的话,我们就需要去了解一下
Swift
的实现了。
具体的可以去看看去年的
WWDC
,
Understanding Swift Performance
。
为了表示一个未知类型的值,编译器会使用一个叫做关联容器的数据结构。在关联容器里,有一个
顺序的缓冲区用来存储小的值。现在我们重新评估了这个缓冲区的大小,但是在
Swift 4
里它还是跟
以前一样是
3
个字。如果值太大了,不能在顺序缓冲区里存储的话,就会在堆上为它分配空间。堆
上的存储当然就是非常昂贵的了,这就是我们上面看到性能一下降低很多的原因。
现在我们改怎么办呢?答案就是关联
“
奶牛
”
缓冲区(
Existential COW Buffer
)。不要误会,不是奶
牛的意思哈,是
copy on right
的缩写。
对于值语义来说的话,这就是高性能的关键。在
Swift 4
中,如果一个值太大不能在顺序缓冲区中存
储的时候,就会在堆上用一个引用计数来给他分配一块空间。只要是只从里面读取数据的话,多个
关联容器都可以共享这个缓冲区。这就避免了很多昂贵的分配堆的开销。如果同时有多个引用指向
它的时候,如果它被修改了,只要再跟它分配一块单独的空间就好了。
Swift
已经自动做了所有这些
复杂的工作。它对性能的影响怎么样呢?它比之前更稳定了。像上面的例子,它就不再需要
18s
而
是只要比
4s
多一点的时间就可以了。
Faster Generic Code
对于明确类型的泛型代码,编译器会使用特定的版本。但是对于类型去不确定的泛型代码,它就会
使用非特定的泛型代码,这也会降低性能。目前为止
Swift
都是用堆来分配非确定类型的泛型缓冲区
的。我们知道,堆是非常慢的,但是在
Swift 4
里,用的是栈来分配泛型缓冲区。对于非特定的泛型
代码来说,会有一些小的性能提升。
Smaller Binaries
下一个性能提升就减少安装包的大小了。方法就是避免将没有用到的方法也放到最终的
ipa
里。这些
都是编译器自动完成的。
Date
结构体遵循了
Equatable
和
Comparable
协议,如果我们只使用到了
Equatable
里的方法,而
没有用到
Comparable
里的方法的话,编译器在编译的时候就不会把
Comparable
里的代码编译进
去。
Unused @objc Thunks
假设我们有下面的类和方法。
在
Swift 3
里面,因为它是从
NSObject
继承的,所以编译器会自动给它加上
objc
的属性,
objc
属
性的目的是说这个方法可以从
OC
来访问。所以编译器就会生成一些方法来从
OC
的调用转成对
Swift
方法的调用。从
Swift
里的调用还是直接访问的。编译器生成的方法叫
thunks function.
。但是
大部分的
thunk function
都是没有用的。但是因为他们是暴露给
OC
的运行时的方法,所以编译器不
知道他们是没有用的。要解决这个问题就要改变语言的模型了。在
Swift 4
里面,
objc
属性只有在
明确需要的时候才会自动进行推断。比如重写了
OC
的方法或者是遵循了
OC
的协议。
这个变化可以减少很多没用的
thunks
。苹果在
Apple Music
里用了这个之后,代码的大小减少了
6
%
。
在从
Swift 3
迁移到
Swift 4
的时候,迁移工具也会提示我们可以进行这一项优化,但如果我们不在
意代码大小的话,也可以不选择这个选项。
迁移工具会自动的在需要加
objc
的地方帮我们加上。有一些可能是迁移工具无法发现的,编译器也
会给出警告。
有的时候可能编译器也没有发现问题,运行时也是会给出警告的。而且会给出具体的代码所在的文
件和行数,只要我们关注
console
输出就可以轻松的解决
objc
的问题。
在完成迁移之后,将
Swift 3 @objc inference
变成
default
就可以了。
Symbol Size
除了无用代码之外,
Swift
的符号也会占用很多空间。
Swift
中用了很多名字很长的符号,通过将名称缩短减少了库的大小。
来对比一下
Swift 4
和
Swift 3
之间的变化。
深蓝色的部分是标准库的大小的变化,还是非常明显的,基本上减少了一半的大小。
Symbol Stripping
链接器用了一个单独的
Trie
结构来查找
symbols
,而且
strip
的选项是默认开启的。
这个过程是在打包的时候进行的,当我们打包的时候会有一个选项让我们选择。
Swift Strings
Swift 4
里处理字符串里的字符会更快更简单,并且可以正确的处理
Unicode
字符。
上面的这个字符有两种编码方式,一种是单个字符
E9
,另外一种就是字母
E
加上一个修饰符。这两
种方式编码的同一个字母。你可能会认为他们是一样的。但不管是在字符数还是在判断他们是否相
等时,他们都是不同的。
但是在
Swift
中的处理方式就不一样了。
Swift
中是以
Unicode
的字形(
grapheme
)的方式来做一
个
character
来处理的。字形指的就是我们在屏幕上看到的样子。不管你用什么方式来组成一个特定
的字形,它都是一个
character
,两个不同组成的字形始终都是相等的。
所以现在将字符串变成字形就变得复杂了很多。