WWDC17 - What's New in Swift

官方视频地址:https://developer.apple.com/videos/play/wwdc2017/402/

在这个近1个小时的视频里,我们看到了 Swift 4 的新特性,Swift 团队的方向以及开源社区的强大力量。

整个视频分为五个小节,分别为:

  • 语言的优化与新功能(Language refinements and additions)
  • 源码兼容性(Source compatibility)
  • 工具与性能(Tools and performance)
  • 标准库(Standard library)
  • 内存的排他性存取(Exclusive access to memory)

下面我们就逐个解析吧。

一、语言的优化与新功能(Language refinements and additions)

  • extension中可以访问到被private修饰的变量。这避免了有时将一些逻辑分离到extension中却访问不了相关的变量,然后不得不将private改成fileprivate的尴尬。😂
  • 类与协议的组合。简单来说就是下面的样子:
var btns: [UIButton & MyProtocol]

&将类名和协议名组合起来,实现一些特定的能力。增加了POP的可用性,喜大普奔!同时,一些 OC 的 API 终于有了完美的 Swift 写法,如视频中的提及的:

类与协议的组合

二、源码兼容性

Swift 4 的与 Swift 3 的差别不大,至少不像从 Swift 2 到 3 这么大。这下大家可以安心升级到 Swift 4 了吧?

在 Xcode 9 中,Swift 会以 4 和 3.2 两个版本兼容存在。我们可以对不同的 target 选择不同的版本进行编译,比如:App 用 Swift 4 编译,而某些第三方库没有更新,依然可以用 Swift 3.2 来编译。

两个版本兼容存在

三、工具与性能

  • 新的 Build System
    更低的性能开销(特别是在大项目上)。使用新的 Build System 目前需要在 Project Settings / Workspace Settings 中手动开启,效果究竟如何有待商榷。
New Build System
  • 预编译的桥接头文件
    在 Xcode 9 中,对混编项目的桥接头文件(bridging header)进行了预编译,生成了一个 precompiled header, 加快了对这些桥接头文件的解析速度。在大项目中这个优化尤为有效,因为我们不再需要在每个 Swift 文件编译时再对桥接头文件编译,也就是说,减少了无谓的、重复的编译。

    这项优化在 Xcode 9 中默认开启。

预编译的桥接头文件
  • 覆盖测试中使用共用构建(Shared Build for Coverage Testing)
    在 Xcode 8 中,执行测试构建会令工程构建两次,其中一次是对整个工程的重新构建,以让其包含冗余测试代码 (extra instrumentation code)从而对代码片段的执行次数进行计数;而另一次构建则是普通的构建,不包含冗余测试代码。

    由于冗余测试代码带来的开销非常小(less than 3%),因此在 Xcode 9 中,编译器对此进行了优化,两次构建变成了一次构建。

  • 构建时索引(Indexing While Building)
    在 Xcode 9 中,烦人的索引过程被挪到了构建时才执行。同样开销非常小。每次构建将更新索引,以实现更精准的索引结果。终于可以跟它说滚蛋了:
万恶的Indexing
  • 可预测的性能(Predictable Performance)

    如果你看过去年 WWDC Session 416 Understanding Swift Performance 的话,应该知道什么是存在容器(Existential Container)

    简单来说,就是协议类型在数组等集合类型中的数据结构。在存在容器中有一个缓存区,占 3 Words 的大小,在64位系统中就是 8 * 3 = 24 Bytes。如果存在容器装得下某个小型的数据结构(比如有2个 Double 的 struct),那这个结构就会被存储在缓存区里;否则会分配到堆中,然后将一个指向该堆地址的指针存储在缓存区(栈)里。

    于是就有了下面这张图:4 Words 的 struct 对比只有 1、2、3 Words 的 struct,会有一个性能开销迅速爬升的情况(performance cliff)。这就是因为涉及到了堆内存的分配。

无法预见的性能瓶颈

视频中还提到:“我们正在重新考量这个内联缓存区的大小,但在 Swift 4 中,它依然是过去一样占 3 个 Words 的大小”。

  • COW 存在容器

    ”那有 performance cliff 怎么办呢?“

    ” 用🐮牛X版存在容器!“

    为了解决这个问题,Swift 团队优化了存在容器,使其有了 COW(copy-on-write,写时复制)能力。如此一来,分配在堆中的缓存区也有了引用计数,多个存在容器可以共用一个缓存区。当要写内存时再根据 COW 的规则来走,减少了堆内存开销。 因此在 Swift 4 中,存在容器将有一个更稳定可靠的性能表现。

    另外,对于泛型也有一项优化:将未具体化的泛型代码(unspecialized generic code)所用的泛型缓存区(generic buffer)的内存分配位置,从原来的堆改成了 Swift 4 中的栈,以实现跟COW存在容器相似的优化效果。

COW优化后的存在容器
  • 更小的二进制包大小(Smaller Binaries)
    减小二进制包大小也就意味着用户的 App 更小。Swift 4 中有这几个方面的优化让我们的 App 瘦身:

    1. 移除未使用的(协议)实现代码。Swift 4 的编译器可分析出哪些(协议)实现代码是没有被使用的,从而在打包时移除掉这些代码。然而这个分析+移除的策略还不是那么完美,需要改动一下 Swift 的语法,也就是下面一项。

    2. 显式写 @objc 关键字以避免自动生成 Objective-C thunk 函数。原来啊,Swift 3 编译时会帮 NSObject 的子类构建一份Objective-C 版本的 thunk 函数以供其 runtime 时调用。这些 thunk 函数虽 然最终还是调用 Swift 的实现,但也占二进制大小,还让(1)中的优化无法实现。因此在 Swift 4 + Objective-C 的混编项目中,需要显式写 @objc 关键字,暴露需要用到的 Swift 方法。如果有多个方法要暴露给 Objective-C,建议你把它们放入 @objc extension 中。

Swift3会自动加入@objc关键字
迁移代码的最后一步
  • 剥除 Swift 符号
    Swift 团队为 libswiftCore 等核心库减小了 symbol 的大小占用,方法是使用更简洁的命名和剥除 Swift 符号。

    至于
    剥除符号(Symbol Stripping)
    的原因,请允许我翻译一下视频中的原话:

"静态链接器和动态链接器分别用自己的字典树来快速查找符号,也就是说 Swift 的 symbols 放在符号表中是基本上没用的。"

因此在 Xcode 9 中,Strip Swift Symbols 默认开启以优化我们工程中自己的 Swift 代码;而对于系统库的 Swift 代码,则在 App Thinning 中才进行符号剥离,同样有一个Strip Swift Symbols的选项可供勾选。

优化后的系统库大小对比
工程中开启 Symbol Stripping
导出时的 Symbol Stripping 选项

四、标准库

这一小节主要讲了Swift 字符串的优化、一些新语法和新的泛型特性。

  • Swift 4 处理复杂字素将更快(是 Swift 3 的3倍效率)。在 Swift 中,一个 Character 即一个字素(Grapheme)。所谓的“复杂字素”,从表面上看,是指如emoji、拉丁字母、汉字、日语假名等非英文亦非简单字符的字素;从底层看,是那些无法通过下标随机存取的字符集合。像这样:

    var famaily = "👩"
    famaily += "\u{200D}👩"
    famaily += "\u{200D}👧"
    famaily += "\u{200D}👦"
    
    print(famaily)    // 👩‍👩‍👧‍👦
    print(famaily.characters.count)   // Swift3输出“4”,Swift4输出“1“
    
  • Swift 4 的字符串本身就是一个字符集合。也就是说,不需要再写 str.characters.count了,直接str.count

  • 一个新语法:str.startIndex...,表示从strstartIndex到其endIndex

    再举个粟子:

String 的 zipping
  • 截取部分字符串的 API 变了,返回的类型也变了。

    let str = "one,two,three"
    
    // Swift 3
    str.components(separatedBy: ",") // 返回 Array<String>
    
    // Swift 4
    str.split(separator: ",") // 返回新类型 Array<Substring>
    

    此举有利有弊。

    利:不再执行深拷贝,减少内存分配和性能开销;

    弊:原来的字符串大哥有可能被其切片后的小弟保持强引用,导致大哥释放不了。

    因此,要在适当的时候显式将Substring转成String

    String与Substring共享缓存.png

  • 使用一对三双引(""")来包装多行字符串字面量。如下图所示,注意缩进:(开源的力量!👏)
三双引
  • 新的泛型特性: protocolassociatedtype可以使用where子句,以实现对不同associatedtype之间的约束。视频给出了 Swift 标准库中利用这个新特性优化了 SequenceCollection的案例。

  • 新的泛型特性:泛型下标(Generic Subscripts)。也就是说可以这样:

    extension Bar {
      subscript<T>(t: T) -> Foo { 
         //... 
      }
    }
    

    视频给出了 Swift 标准库中利用这个新特性实现了(上面第3点)的案例。

五、内存的排他性存取(Exclusive access to memory)

这是一个 Swift 4 的新特性,简单而言就是,对有值语义(value types)集合类型变量的写操作时,不能同时再触发另一读写操作。或者也可以理解成,不能再触发 copy-on-write 机制。

内存的排他性存取

也就是说,不让下面这种事发生!在迭代时,闭包引用了numbers的缓存区,希望修改numbers内元素的值。如果此时执行numbers = [],那就会报错:排他性存取!

非排他时触发了COW

排他性存取的检测分为编译时运行时两种。编译时检测可直接报错;运行时检测则会抛出异常,如下图。

运行时报错

有点像多线程的问题对吧?至此,我们只是在单线程下看这个排他性存取,而如果在多线程下触发运行时的排他性存取,那就要通过 Thread Sanitizer 处理了。

当前,在 Swift 3.2 下的非排他性存取只会报 warning,但在未来的 Xcode 中会升级为 error。

内存的排他性存取保证了安全性的同时,也为从中优化了标准库的设计。

在工程设置中可以调整 Exclusive Access to Memory 的策略。

WX20170613-211406@2x.png

总结

Swift 4 在 Swift 3 的基础上升级,没有像去年那样巨大的迁移成本。升级后的 Swift 更快速、更安全,配合着 Xcode 9,生成的 App 体积将更小。

最后

此文粗略,或有错漏,烦请指明,当天修正!

终于写完了!全程无字幕听着画重点,哈哈!🐶
Let's Swift!🎉

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

推荐阅读更多精彩内容