我们按照基本概念的主题来组织本书,其中有一些深入像可选值和字符串这样基本概念的章节,也有对于像C语言互用性方面的主题。不过纵观全书,有一些主题可以描绘出Swift给人的总体印象:
Swift既是一门高层级语言,又是一门低层级语言。 你可以在Swift中用map或者reduce来写出十分类似于Ruby和Python的代码,你也可以很容易地创建自己的高阶函数。Swift让你有能力快速完成代码编写,并将它们直接编译为原生的二进制可执行文件,这使得其在性能上可以与用C语言编写的程序相媲美。
Swift真正激动人心,以及令人赞叹的是,我们可以 兼顾 高低两个层级。将一个数组通过闭包表达式映射到另一个数组所编译得到的汇编码,与直接对一块连续内存进行循环所得到的结果是一致的。
不过,为了最大化地利用这些特性,有一些知识你需要掌握。如果你能对结构体和类的区别有深刻理解,或者对动态和静态方法派发的不同了然于胸,那么你就能从中获益。我们会在之后更深入地介绍这些内容。
Swift是一门多范式的语言。 你可以用Swift来编写面向对象的代码,也可以使用不变量的值来写纯函数式的程序,在必要的时候,你甚至还能使用指针运算来写和C类似的代码。
这是一把双刃剑。好的一面,在Swift中你将有很多可用的工具,你也不会被限制在一种代码写法里。但是这也让你身临险境,因为实际上你可能会变成使用Swift语言来书写Java或者C或者Objective-C的代码。
Swift仍然可以使用大部分Objective-C的功能,包括消息发送,运行时的类型判定,以及KVO等。但是Swift还引入了很多Objective-C中不具备的特性。
Erik Meijer是一位著名的程序语言专家,他在2015年10月“发推” [3] 说道:
现在,相比Haskell,Swift可能是更好,更有价值,也更合适用来的学习函数式编程的语言。
Swift拥有泛型、协议、值类型以及闭包等特性,这些特性是对函数式风格的很好的介绍。我们甚至可以将运算符和函数结合起来使用。在Swift早期,这门语言为世界带来了很多关于单子(monad)的博客。不过等到Swift2.0发布并引入协议扩展的时候,大家研究的趋势也随之发生了变化。
Swift十分灵活。 在On Lisp [4] 这本书的介绍中,Paul Graham写道:
富有经验的Lisp程序员将他们的程序拆分成不同的部分。除了自上而下的设计原则,他们还遵循一种可以被称为自下而上的设计,他们可以将语言进行改造,让它更适合解决当前的问题。在Lisp中,你并不只是使用这门语言来编写程序,在开发过程中,你同时也在构建这门语言。当你编写代码的时候,你可能会想“要是Lisp有这个或者这个运算符就好了”,之后你就真的可以去实现一个这样的运算符。事后来看,你会意识到使用新的运算符可以简化程序的某些部分的设计,语言和程序就这样相互影响,发展进化。
Swift的出现比Lisp要晚得多,不过,我们能强烈感受到Swift也鼓励从下向上的编程方式。这让我们能轻而易举地编写一些通用可重用组件,然后可以将它们组合起来实现更强大的的特性,最后用它们来解决实际问题。Swift非常适合用来构建这些组件,你可以使它们看起来就像是语言自身的一部分。一个很好的例子就是Swift的标准库,许多你能想到的基本组件——像可选值和基本的运算符等——其实都不是直接在语言本身中定义的,相反,它们是在标准库中被实现的。
Swift代码可以做到紧凑、精确,同时保持清晰。 Swift使用相对简洁的代码,但这并不意味着其单纯地减少代码的输入量,还标志了一个更深层次的目标。Swift的观点是,通过抛弃你经常在其他语言中见到的模板代码,而使得代码更容易被理解和阅读。这些模板代码往往会成为理解程序的障碍,而非助力。
举个例子,有了类型推断,在上下文很明显的时候我们就不再需要乱七八糟的类型声明了;那些几乎没有意义的分号和括号也都被移除了;泛型和协议扩展让你免于重复,并且把通用的操作封装到可以复用的方法中。这些特性最终的目的都是为了能够让代码看上去一目了然。
一开始,这可能会对你造成一些困扰。如果你以前从来没有用像是map、filter和reduce这样的函数,那么它们可能看起来比简单的for循环要难理解。但是我们相信这个学习过程会很短,并且作为回报,你会发现这样的代码你第一眼看上去就能更准确地判断出它“显然正确”。
除非你有意为之,否则Swift在实践中总是安全的。 Swift和C或者C++这样的语言不同,在那些语言中,你只要忘了做某件事情,你的代码很可能就不是安全的了。它和Haskell或者Java也不一样,在后两者中有时候不论你是否需要,它们都“过于”安全。C#的主要设计者之一Eric Lippert在他关于创造C#的10件后悔的事情中总结了 [5] 一些经验教训:
有时候你需要为那些构建架构的专家实现一些特性,这些特性应当被清晰地标记为危险——它们往往并不能很好地对应其他语言中某些有用的特性。
说这段话时,Eric特别所指的是C#中的终止方法( fi nalizer),它和C++中的析构函数(destructor)比较类似。但是不同于析构函数,终止方法的运行是不确定的,它受命于垃圾回收器,并且运行在垃圾回收的线程上。更糟糕的是,很可能终止方法甚至完全不会被调用。但是,在Swift中,因为采用的是引用计数,deinit方法的调用是可以确定和预测的。Swift的这个特点在其他方面也有体现。未定义的和不安全的行为默认是被屏蔽的。比如,一个变量在被初始化之前是不能使用的,使用越界下标访问数组将会抛出异常,而不是继续使用一个可能取到的错误值。
当你真正需要的时候,也有不少“不安全”的方式,比如unsafeBitcast函数,或者是UnsafeMutablePointer类型。但是强大能力的背后是更大的未定义行为的风险。比如下面的代码:
var someArray=[1,2,3]
let uhOh=someArray.withUnsafeBufferPointer{ptr in
//ptr只在这个block中有效
//不过你完全可以将它返回给外部世界:
return ptr
}
//稍后...
print(uhOh[10])
这段代码可以编译,但是天知道它最后会做什么。方法名里已经警告了你这是不安全的,所以对此你需要自己负责。
Swift是一门独断的语言。 关于“正确的”Swift编码方法,作为本书作者,我们有着坚定的自己的看法。你会在本书中看到很多这方面的内容,有时候我们会把这些看法作为事实来对待。但是,归根结底,这只是我们的看法,你完全可以反对我们的观点。Swift还是一门年轻的语言,许多事情还未成定局。更糟糕的是,很多博客或者文章是不正确的,或者已经过时(包括我们曾经写过的一些内容,特别是早期就完成了的内容)。不论你在读什么资料,最重要的事情是你应当亲自尝试,去检验它们的行为,并且去体会这些用法。带着批判的眼光去审视和思考,并且警惕那些已经过时的信息。