WWDC 2014 - Swift 语言测评

  2014-11-20 00:00:00 CST

  Jingwen Peng

  WWDC Swift Objective-C

Swift-Logo

又是一年 WWDC,2014 年苹果带来全新设计的 Mac OS X Yosemite 和更加开放的 iOS 8,除此之外更重要的是发布了新的编程语言 Swift。以往几乎每年苹果都会对 Objective-C 进行小幅升级,增加一些方便的新语法和新特性,但是 Swift 的发布着实让人出乎意料。Swift 发布之后会有几年的过渡期,开发者不会立刻停用 Objective-C,但是 Objective-C 这门我最喜爱的语言终将会被淘汰,还好 Swift 继承了 Objective-C 大部分优秀特性,毕竟人总要往前看,发展的总是美好的。

Swift 是一种用于 iOS 和 Mac OS X 应用开发的全新编程语言,即建立在优秀的 C 和 Objective-C 之上,又没有 C 语言的兼容性限制。有人说 Swift 是没有指针的 Objective-C,差不多是这个意思,但又不能完全这么说。Swift 采用了安全的编程模式,使用起来也更加灵活和可靠,也解决了一些 Objective-C 的硬伤。对于开发者来说,Swift 并不陌生,因为苹果不惜花费大力气为 Cocoa Framework 和 Cocoa Touch Framework 编写一套完整的 Swift API,既可以无缝使用现有的成熟框架,也可以和 Objective-C 混合编程,而且提供了相当好用的 Playground 功能,Swift 已经不只是一个在开发中的语言,Swift 已经成熟到可以在生产中使用了。伴随着 Swift 发布的还有 Xcode 6 和一本 500 多页的语言指导,这本指导书在苹果开发者文档和 iBooks 商店都可以找到。


回顾 Objective-C

NeXTSTEP

面向对象的编程理念是在 NeXTSTEP 那个时代兴起的,支持面向对象的语言并不多,Objective-C 似乎是一个不错的选择,即兼容传统的 C 语言代码,又支持面向对象的特性。但是最终 Objective-C 并没有成为主流,似乎只有 NeXT 采用了 Objective-C,而正是如此,NeXT 主导了 Objective-C 的发展,将其变成了一门专为自己服务的语言。后来苹果收了 NeXT,Objective-C 逐渐走向狭隘,随着 Cocoa 和 Cocoa Touch 的使用,Objective-C 也变成了苹果开发的专用语言。苹果也在持续地改进其开发语言,比较有代表性的就是引入 GC(Garbage Collection),以及 GC 的替代品 ARC(Automatic Reference Counting),其开发工具 Xcode 也在逐步进化,开发者们的开发环境足够好用,而且也越来越舒适,他们也很满意苹果的这套游戏规则。

但是任凭优雅的 Objective-C 再怎么进化,有些东西还是无法改变。Objective-C 本质上就是 C 的一个扩充,有过开发经验的人都懂,如果写苹果的应用,满脸都是指针,比如一个 id 可以指向任何对象,这意味着极大的灵活性,当然也是问题的根源。尝试去扩展 Objective-C,让它支持更多更现代的特性并不是不可能,苹果成功的让 Objective-C 支持 closure 就是一个很好的例子,但是无论怎样的扩展都无法改变 C 内在的不安全,而 Objective-C 和 Cocoa Framework 的密切关系也使得很直接将现有的语言拿来就用,Swift 诞生也许是最好的选择。


Swift 发布之前的准备

苹果早就在酝酿 Swift,在近几年的 WWDC 上从对 Objective-C 的改进不难看出,苹果一直在试图改变人们的代码习惯,尝试向着更简洁更易用的形式改变,增加了许许多多的语法糖和新功能,这确实有点让 Objective-C 面目全非了,好像用起来不是以前的感觉了,味道不对了,但这一切也许都是为了 Swift 做准备。

@synthesize

比如 WWDC 2012 为 Objective-C 带来了许多新的简洁语法,属性合成是一个有代表性的例子。 例如如果在 .h 文件中有这样的一个属性

1
@property (strong) NSString *dataString;

你就可以在 .m 文件中使用 @synthesize 来直接合成 setter 和 getter,节省了大量不必要的输入

1
@synthesize dataString = _dataString;

这些新的语法特性也都更现代,苹果在推崇现代的编程方式,也是在为 Swift 铺路。

Module

例如 WWDC 2013 增加了 Module,可以轻松便捷的导入框架。 之前如果要加入一些框架,比如 iAd、GameCenter 等,简单的 import 进来是不行的,会编译失败

1
#import <iAd/iAd.h>

得在工程设置里面,像这样引入框架

Build-With-Libraries

这样就显得繁琐,很不易于使用,进化过后的 Objective-C 支持下面的语句,可以直接导入框架

1
2
3
@import iAd; // 全部导入
@import iAd.ADBannerView; // 选择性导入
#import <iAd/ADBannerView.h> // 支持这种语句,兼容老的代码

另外由于 Modules 改变了引入的模式,使用新的机制能够一定程度提升性能。开发只需使用新的 Xcode,然后开启 Modules,一切就 OK 了,不需要做任何的代码修改。

好歹我这也算个技术博客,光这样说说表面现象怎么能行,我们来看看 Module 背后发生了什么。

~/Library/Developer/Xcode/DerivedData/ModuleCache/ 下可以找到一些存放有 .pcm 文件的目录。PCM 即 Pulse Code Modulation(脉冲编码调制),这是什么鬼?开个玩笑,这个 PCM 是指 PreCompiled Module(预编译模块),Xcode 将 Mac OS X、iOS 和你自己的 Framework 进行了一定程度的预编译,将预编译后的信息存储于此,上面提到的直接引入方式也就是在这寻找相应的模块,从而省去在工程中的 Build Phrases 中手动挨个引入 Framework。同时因为采取了预编译,这种 Module 机制能够提高性能也就理所当然了吧。但是这名字怎么乱叫,一会儿是 Framework 一会儿又是 Module,这个应该叫 FrameworkCache 才对啊,而且这跟 Swift 有个什么关系?别急,这个问题稍后说到。

ModuleCache


Swift 发布

想知道 Swift 的发布到底有多么激动人心,建议去观看视频录像,在此视频集中观看名为 Keynote 的视频。

同时发布的也有一本语言指导,应该是学习 Swift 的最佳资料了吧。如果要阅读,请注意一定不要去阅读中文翻译版的,因为 iBooks 商店中上架的《The Swift Programming Language》,一直在修订和改变,而有些国人翻译的中文版则始终停留在第一版,读那个老版本基本会被坑,所以强烈建议去 iBooks 下载或者去 https://developer.apple.com 阅读网页版的语言指导。

当然 Swift 对于苹果开发者来说,并不是一个新世界,更不是要坑死开发者,从 Objective-C 到 Swift 更多的是仅仅是语言的变化,还有编程习惯和编程模式的改变,而 Cocoa 还是那个熟悉 Cocoa,就像换了一把新筷子吃同样的饭,而且换的是双特别好用的新筷子。如果是想学习 Mac OS X 和 iOS 的应用开发,至少在现在来说,还是建议先去学习 Objective-C 再考虑尝试 Swift。


Swift 带来的变化

这里尽量不谈语言本身有什么新特性,这些一句两句说不完,如果想了解或者深入学习,去阅读语言指导是个更好的选择,这里说一些容易引起困惑的和好玩的东西。

Class Prefix 去哪了

先不说语言本身有多神奇,Swift 正在改变 Objective-C 开发者的习惯,给我感触最深的是默认不再使用 Class Prefix,Objective-C 的 Class Prefix 要成为过去了。在老版本的 Xcode 中如果新建项目会提示设置类前缀,因为 Objective-C 中并没有类似于 Namespace 或者 Package 的设计,命名冲突主要靠类前缀解决。苹果的 Foundation 和 Cocoa 框架中的 NSString、NSArray、NSDictionary 等都有前缀 NS,NS 是 NeXTSTEP 的缩写,现代的 Mac OS X 大部分继承自 NeXTSTEP。

下图前一张为 Xcode 5,后一张为 Xcode 6

ClassPrefix-Xcode5

ClassPrefix-Xcode6

新版的 Xcode 中弃用了 Class Prefix,原因应该有两点:一、在应用中有一部分代码是用来控制界面和交互的,这部分代码基本上是不可重用的,是与项目密切相关的,类前缀对于它们来说毫无意义;二、Swift 采用 Module 的方式,在项目中每一个 Target 就是一个 Module,可以完全解决命名冲突的问题。当然如果你怀念类前缀,一样可以在项目创建后,去设置中打开。

IBModule

在新的 Interface Builder 中增加了 Module 项,扔掉类前缀,使用 Module 可以很好的解决命名空间的问题,但是这种引用风格就显得诡异了,难道代码要这样写?

1
NeXTSTEP.String(string: "https://pjw.io")

事实上并没有这样做,类前缀的方便易用是毋庸置疑的,即使是在 Swift 时代,类前缀依然没有被完全放弃。若要使用 Framework 里面的类,依然使用老的习惯,当然如果真的遇到了冲突,则可以通过加上 Framework 前缀来进行区分。

1
2
NSString(string: "https://pjw.io")
Foundation.NSString(string: "https://pjw.io")

另外,苹果鼓励开发者在自己的应用中多使用框架,将那些可以复用并且与 UI 和 UX 无关的东西放入 Framework,而且使用 Framework 也可以进行方便的版本管理和平台移植。在 Framework 之外的代码尽量不使用类前缀,因为这部分代码基本不具有重用性;而在 Framework 之外的代码尽量使用类前缀,为的是可以不那么麻烦的写代码,否则使用的时候需要加上 Module 前缀。Module 不但解决了 Objective-C 没有命名空间的硬伤,而且解决地很优雅。

现在应该清楚了前面关于 Module 的问题了吧,为什么是 ModuleCache 而不是 FrameworkCache。如果还是不清楚,来看看《The Swift Programming Language》是怎么说的。

A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword. Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift. 一个 Module 就是一个代码分发的单元,它可以是一个框架或者一个应用,可以被 import 关键字导入到其他的 Module 中。使用 Swift 时,Xcode 中每一个编译的 Target(比如应用或者是框架)都被看成是不同的 Module。

一个更安全的语言

相比起 Objective-C,Swift 当然是更安全了,其安全表现在方方面面,其中 Optional 是让人眼前一亮的东西。在没有特殊说明的情况下,Swift 的变量是一定不为空的。

1
2
3
4
// 变量 s 不可以为空
var s = "https://pjw.io"
// 试图将其赋空值会出错
s = nil

这样就可以放心大胆得去用,不用担心是否会出现空指针错误,如果需要变量可以为空,则需要 Optional Type。如果变量类型为 Optional Type,则它就可以为空值。

1
2
3
// 变量 s 可以为空
var s: String? = "https://pjw.io"
s = nil

如果一个变量是 Optional Type,可以使用 Optional Binding 来快速安全的使用它。

1
2
3
4
5
6
var s: String?
......
// 如果 s 不为空,则将 s (String?) 解包为 ss (String)
if let ss = s {
    println(ss)
}

强制类型不为空和引入 Optional 不但能使代码更高效,而且也消灭了一些由于疏忽而带来的 Bug。其他的比如默认不进行 fall through 的 switch 语句等小细节,也在处处体现着 Swift 是一个安全的语言。

Playground

Playground 也许是最能带给开发者惊喜的一个新特性,这是一个用来试验和测试的神器。不用多说,试过之后就会产生共鸣,会发现自己一直需要这么个东西。Swift 发布时的 Playground 演示也相当精彩。

使用 Playground 可以做到写代码的同时就能看到效果,想要用它写一个应用是不现实的,用它去做一些代码测试工作就显得相当方便。比如测试自己的 Framework,可以直接用 import 关键字将自己的 Framework 导入,然后随手就写几行测试,写完就能看到效果;再比如不熟悉某个类的用法,在 Playground 写几句看看,写出合适的代码片段后再直接复制到应用中去。在没有 Playground 的时候,我们大家其实也都在这么干,在没有把握的时候,在测试里面写一小段代码,然后不断地去运行然后修正代码,甚至是新建一个工程,写几段代码跑跑看,写出自己想要的东西后再复制到应用中去。有了 Playground,工作高效会比以往高很多。

Balloons-Playground


Swift 准备好了吗

从发布至今,Swift 的语言指导已经修订多次,Xcode 也经历了几次的升级。

Xcode-6-1

但是似乎 Xcode 并不是很稳定,反正我是经常碰到这个。

SourceKitCrash

Swift 并没有那么的 Swift(迅速)。在编译的速度上,同样的程序,如果使用 Swift 编写,则其编译的速度明显要比 Objective-C 慢上不少,因为中间经历的更多的步骤。编译后的程序运行速度则没有太大不同,说到底都是 Cocoa 的 API,都使用 LLVM 编译器,会有不同,但是差异不会很大。

现在 Swift 还并不完备,苹果在努力的改进它,新的功能特性或者语言修正随时可能发生,毫无疑问的是,Swift 终将取代 Objective-C 成为苹果应用开发的主流语言。现阶段 Swift 已经成熟到可以拿来使用了。也有越来越多的开发者开始尝试使用 Swift,比如 Mozilla 的 Firefox for iOS 已经使用 Swift 开始开发。

从 Objective-C 切换到 Swift,总是有些失落的,在这里要向 Objective-C 说再见了,还是十分怀念 Objective-C 那看起来特别高冷的[中括号]。说到底 Swift 也只是一门新语言而已,也许再过十年或者二十年,苹果又会换语言了。Swift 的出现并不意味着你零基础就轻松搞定 iOS 或者 Mac OS X 的应用开发,它也没有坑死苹果开发者,更不会像有些人所说的 “Swift 正在颠覆整个互联网生态”。如同 Go 语言,Swift 是一门相当现代的编程语言,也代表着一种现代的编程方法。这应该是更健康的编程方式,代码量更少,需要的思考更多,尽量不要让将工程师有限的精力浪费在一些没有意义的代码上。

补充

上面提到的 SourceKit 崩溃 Bug 已经在 Xcode 6.1.1 (6A2008a) 中修复。

XcodeFix

如果您有疑问或建议,请在下方评论区域留言

遵循 BY-NC-ND 协议

评论功能加载中...