原文链接:http://www.cocoachina.com/applenews/devnews/2014/0617/8857.html

假设你和我一样,准备好好看看苹果的 Keynote,兴奋地准备開始尝试一些新的 API,结果你听到最多的是一门新的语言:Swift!你突然被告知,这不是 Objective-C的扩展。而是一门完全然全新的语言。你是会激动呢,还是高兴。抑或头脑一片空白?
 
Swift 将会全然改变我们写 iOS 和 Mac 应用的方式,在这片文章里,我概括了这门语言的一些要点,并和 Objective-C 里面对应部分做了对照。
 
注意:这不是 Swift 的入门读物。苹果已经公布了一本非常全面的 Swift
Programming Language
,我强烈建议你先读它。这篇文章仅仅会讨论一些特别 cool、值得玩味的知识点。

 
类型
第一个重大的事情是 Swift 提供了类型判断 (type inference)。假设语言本身能够判断类型。那么开发人员就再也不必声明每个变量的类型了。编译器能够搞定这件事,如以下的样例,编译器能够自己主动把变量类型设为
String:
  1. // automatically inferred
  2. var name1 = "Matt"
  3. // explicit typing (optional in this case)
  4. var name2: String = "Matt"
与类型判断相伴而来的,Swift 提供了类型安全的保证 (type safety)。


Swift 中,编译器(大部分情况如此,偶有例外)知道一个对象的完整类型。这让它能够做决定来怎么编译代码。

 
这和 Objective-C 是大相径庭的。Objective-C 是一门很动态的语言,在编译期对象的类型并不全然清楚。部分原因是由于,你能够在执行时给既存的类型添加新的方法、添加一个完整的新类、甚至改变一个实例的类型。
 
让我们看一个详细的样例,以下的 Objective-C 代码:
  1. Person *matt = [[Person alloc] initWithName: @"Matt Galloway"];
  2. [matt sayHello];
当编译器看到 sayHello 被调用的时候。它可以检查头文件中面是否有这种方法的声明,假设没有则编译会出错。可是它能做的仅此而已。这通常情况下是足够了,可以用来发现问题最先出现的地方,以及出现的打字错误。可是由于动态性。编译器不知道 sayHello 是否会在执行时被改动或者一直存在。譬如。它可能是某个协议的一个可选方法(记住 respondsToSelector: 检查)。
 
由于不是强类型的。所以 Objective-C 编译器能做的就比較有限,无法在方法调用的时候进行一些优化。众所周知,方法调用都是通过 objc_msgSend 来动态 dispatch 的,你应该在 backtrace 里看到过。对这个函数而言,选择器的实现会经过查找和跳转两步。我们不可否认这会添加额外开销和复杂度。
 
我们再来看看 Swift 如何完毕相同的事情:
  1. var matt = Person(name: "Matt Galloway");
  2. matt.sayHello()
在 Swift 中,编译器知道很多其它类型信息,它能确切地知道 sayHello 在哪里被定义。因此,它能做一些优化而直接跳转到方法的实现入口,而不必经由动态 dispatch。在其它语言中,可能会通过 vtable 来进行 dispatch,这会比 Objective-C 採用的动态 dispatch 代价稍小一些,C++ 的虚函数就是这样做的。
 
在 Swift 中,编译器能提供很多其它帮助。他能阻止模糊类型带来的 bug。也能通过编译优化让你的代码跑得更快。
 
泛型
还有一个重大的功能就是泛型。假设你对 C++ 比較熟悉,那你可能认为泛型 (generic) 和模板 (templates) 比較像。由于 Swift 有严格的类型限制,你声明一个函数的时候。对于它的參数必须指明白定的类型。

但有时候你对于不同的数据类型。可能都须要相似的功能。

 
一个样例是通常很实用的数据结构 Pair,你须要将一对值存放在一起。对于整数,你能够这么干:
  1. struct IntPair {
  2. let a: Int!
  3. let b: Int!
  4. init(a: Int, b: Int) {
  5. self.a = a
  6. self.b = b
  7. }
  8. func equal() -> Bool {
  9. return a == b
  10. }
  11. }
  12. let intPair = IntPair(a: 5, b: 10)
  13. intPair.a   // 5
  14. intPair.b   // 10
  15. intPair.equal()  // false
看起来还比較实用。可是如今你想让它也能用在浮点数上,你可能就须要再定义一个 FloatPair 的类,这看起来就有点丑陋了。这时候泛型就能派上用场了,与再定义一个新的类相比,你也能够:
  1. struct Pair {
  2. let a: T!
  3. let b: T!
  4. init(a: T, b: T) {
  5. self.a = a
  6. self.b = b
  7. }
  8. func equal() -> Bool {
  9. return a == b
  10. }
  11. }
  12. let pair = Pair(a: 5, b: 10)
  13. pair.a  // 5
  14. pair.b  // 10
  15. pair.equal()  // false
  16. let floatPair = Pair(a: 3.14159, b: 2.0)
  17. floatPair.a   // 3.14159
  18. floatPair.b   // 2.0
  19. floatPair.equal() // false
这就相当实用了!

可能这还不够清楚说明你如今为什么要使用这一功能,可是相信我:机会无限!你非常快就会在你的代码中用到它了。

 
容器
你应该已经明确而且喜欢使用 NSArray,NSDictionary 以及他们的可改动版本号了,如今你会看到他们在 Swift 中的等价物。

幸运的是,他们很相像。你能够这样来声明一个数组和字典:

  1. let array = [1, 2, 3, 4]
  2. let dictinary = ["dog": 1, "elephant": 2]
这对你来说应该再熟悉只是,仅仅是有些许差异:在 Objective-C 中。仅仅要你愿意。数组和字典中能够放入不论什么类型的对象,可是在 Swift 里面。数字和字典必须指定数据类型,这都是通过前面所讲的泛型实现的。
 
上面的两个变量也能够这样定义,带上类型信息(记住:虽然你不必这么做):
  1. let array: Array = [1, 2, 3, 4]
  2. let dictinary: Dictionary = ["dog": 1, "elephant": 2]
注意看在定义哪些类型能够放入容器时。泛型的使用。对于数组另一个简略的形式。看起来可读性更好。但本质上是做相同的事情:
  1. let array: Int[] = [1, 2, 3, 4]
注意:如今会禁止不论什么 Int 之外的实例增加这个数组。

这听起来不那么方便,可是毋庸置疑地实用:你的 API 再也不用长篇累牍地解释它返回的数组中会保存什么内容。或者一个属性里面可以保存什么内容,你可以把这些问题交给编译器,由它来进行前期的错误检查和优化。是更明智的做法。

 
可变性
Swift 中的容器的一个有意思的地方就是它的可变性。

与 Objective-C 不一样。Array 和 Dictionary 并不存在一个可变版本号。你仅仅能通过 let 和 var 来区分它们。对于那些还没有读过原书,或者还未深入 Swift 的读者(我建议你们读一下。尽快!

),仅仅须要知道 let 用来声明常量。var 用来声明变量。let 有点类似于 C/C++/Objective-C 中的 const。

 
用 let 声明的容器无法改变它的大小,也就是说不能调用追加和删除方法,假设你这样做,就会得到类似错误:
  1. let array = [1, 2, 3]
  2. array.append(4)
  3. // error: immutable value of type 'Array' only has mutating members named 'append'
这相同适用于字典类。这导致编译器能够推导集合的性质并做适当优化。

比方,假设大小不能改变,那么已经保存的值就永远不用考虑又一次分配以接纳新值。所以,对于不会发生变化的集合对象,总是使用 let 来声明是一种非常好地做法。

 
字符串
Objective-C 中的字符串是众所周知的难用。就算仅仅是连接字符串。代码也是很冗长。比如:
  1. Person *person = ...;
  2. NSMutableString *description = [[NSMutableString alloc] init];
  3. [description appendFormat:@"%@ is %i years old.", person.name, person.age];
  4. if (person.employer) {
  5. [description appendFormat:@" They work for %@.", person.employer];
  6. } else {
  7. [description appendString:@" They are unemployed."];
  8. }
这样的代码太啰嗦了,里面包括了太多与目的无关的字符。

相同的事情 Swift 里面做起来就是这样:

  1. var description = ""
  2. description += "\(person.name) is \(person.age) years old."
  3. if person.employer {
  4. description += " They work for \(person.employer)."
  5. } else {
  6. description += " They are unemployed."
  7. }
清晰很多。注意格式化一个字符串的方法,你如今也能够通过 += 操作符来连接简单连接两个字符串。这都简洁太多。而且,也没有可变、不可变两种字符串存在。
 
Swift 还有一个很好的增强是字符串比較。你应该知道,Objective-C 中使用 == 操作符并不能比較两个字符串是否相等,你必须使用 isEqualToString: 这种方法,这是由于 == 仅仅是比較两个指针是否相等。Swift 抛弃了这些概念。让你直接通过 == 操作符来直接比較内容是否相等,这也意味着字符串也能够用到 switch 语句中。下一节会介绍很多其它相关内容。
 
最后一则好消息就是 Swift 原生支持全部 Unicode 字符,你能够在字符串中使用不论什么 Unicode 代码点,甚至在函数名和变量名中,假设你愿意,你能够给一个函数命名为(pile of poo!)。

 
对 Unicode 支持的还有一个很方便的地方,就是提供了内建方法。来计算一个字符串的真实长度。一旦支持全范围的 Unicode 字符,字符串长度的计算就不再是一件简单的事情。在 UTF8 字符串里。你不能把字节数当成字符串长度,由于有些字符会占用超过一个字节的空间。

在 Objective-C 中,NSString 总是依照 UTF16 来计算长度。把每两个字节当成一个字符,但技术上说这并不总是正确的,由于有些 Unicode 字符会占用 2 个以上的字节。

 
幸运的是。Swift 提供了一个很方便的函数来计算代码点的真实个数,这就是名叫 countElements 的顶级函数。你能够像这样来使用它:
  1. var poos = "? ? ? ??? ?? "
  2. countElements(poos) // 2
但这也并非对全部情况都成立,它仅仅仅仅统计了一下 Unicode 代码点的个数。并没有考虑引起字符变化的特殊情形。比如,你能够加一个元音符号到一个前缀字符上,这时候 countElements 会觉得是两个字符,而实际上看起来仅仅有一个字符:
  1. var eUmlaut = "e\u0308" // ë
  2. countElements(eUmlaut) // 2
说一千道一万,我想你肯定会赞成,相比 Objective-C。Swift 中的字符串好用太多!
 
Switch 语句
我想讲的最后一件事情就是 switch 语句,与 Objective-C 相比,Swift 对它做了彻底的改进。这是一件非常有意思的事情。由于涉及到一些东西,假设不从根本上打破 Objective-C 是 C 的严格超集这一事实,就无法增加到 Objective-C 中去。
 
第一个让人兴奋的点是 switch 能够用到字符串上了,这可能是你曾经想做但做不到的。在 Objective-C 中,你仅仅能通过大量的 if 语句和 isEqualToString 组合来达到类似效果。比如:
  1. if ([person.name isEqualToString:@"Matt Galloway"]) {
  2. NSLog(@"Author of an interesting Swift article");
  3. } else if ([person.name isEqualToString:@"Ray Wenderlich"]) {
  4. NSLog(@"Has a great website");
  5. } else if ([person.name isEqualToString:@"Tim Cook"]) {
  6. NSLog(@"CEO of Apple Inc.");
  7. } else {
  8. NSLog(@"Someone else);
  9. }
这种代码可读性太差,而且有太多的字符要敲入。而相同的事情,在 Swift 中则:
  1. switch person.name {
  2. case "Matt Galloway":
  3. println("Author of an interesting Swift article")
  4. case "Ray Wenderlich":
  5. println("Has a great website")
  6. case "Tim Cook":
  7. println("CEO of Apple Inc.")
  8. default:
  9. println("Someone else")
  10. }
除了字符串上的 switch 之外。另一些有意思的地方须要注意。这里没有不论什么 break,这是由于 case 并不会一直运行下去。碰到下一个 case 的时候自己主动就退出了,这样能够降低好多不小心引发的 Bug。
 
以下的 switch 语句可能会让你头脑发蒙,请做好准备:
  1. switch i {
  2. case 0, 1, 2:
  3. println("Small")
  4. case 3...7:
  5. println("Medium")
  6. case 8..10:
  7. println("Large")
  8. case _ where i % 2 == 0:
  9. println("Even")
  10. case _ where i % 2 == 1:
  11. println("Odd")
  12. default:
  13. break
  14. }
首先,这里出现了一个 break 语句。这是由于 switch 须要全然穷举,比方须要处理全部分支。在这里,我们默认是什么都不做,所以使用 break 来明白声明这一意图。

 
其次。这里有... 和..,这两个用来定义范围的操作符。前一个表示闭区间(包括最右边的边界值),而后一个则表示开区间(不包括最右边的边界值),这将会带来难以置信的便利。
 
最后,能够直接把 case 的分支变量作为计算的输入。以上为例。假设一个数字不在 0-10 范围内,那么对于偶数会打印「Even」,而对于奇数则会打印出「Odd」。奇妙到爆。有没有。
 
接下来
希望这篇文章能让你初尝 Swift 的滋味,也能让你明确后面蕴藏了多少宝藏,可是还有很多其它事情要做。我强烈赞成你去读一下苹果公司的书,看看其它文档,以帮助你来深入学习这门语言——你迟早须要这么做!

swift 新功能介绍的更多相关文章

  1. CentOS以及Oracle数据库发展历史及各版本新功能介绍, 便于构造环境时有个对应关系

    CentOS版本历史 版本 CentOS版本号有两个部分,一个主要版本和一个次要版本,主要和次要版本号分别对应于RHEL的主要版本与更新包,CentOS采取从RHEL的源代码包来构建.例如CentOS ...

  2. 原创开源项目HierarchyViewer for iOS 2.1 Beta新功能介绍

    回顾 HierarchyViewer for iOS是我们发布的一个开源项目,采用GPL v3.0协议. HierarchyViewer for iOS可以帮助iOS应用的开发和测试人员,在没有源代码 ...

  3. fedora21发布与新功能介绍(附fedora21安装教程与fedora21下载地址)

    fedora21发布与新功能介绍(附fedora21安装教程与fedora21下载地址) 最新的Fedora 21终于正式发布了,Fedora Server 是一款强大可定制化的操作系统,包括了最好最 ...

  4. Unity User Group 北京站:《Unity5.6新功能介绍以及HoloLens开发》

    ​时间一转眼从春天来到了初夏,Unity User Group(以下简称UUG)活动也迎来了第七期.我们面向Unity3D开发从业者以及未来想从事Unity3D开发的学生群体的UUG活动这次仍然在海淀 ...

  5. Eviews 8.0&9.0界面新功能介绍

    Eviews 8.0&9.0界面新功能介绍 本文其中一些是自己的整理,也有一些是经管之家论坛中一位热心.好学坛友的整理,其中只是简单介绍一下这两个新版本的部分特性,分享出来,有兴趣的看客可以一 ...

  6. Kafka 0.11新功能介绍:空消费组延迟rebalance

    Kafka 0.11新功能介绍:空消费组延迟rebalance 在0.11之前的版本中,多个consumer实例加入到一个空消费组将导致多次的rebalance,这是由于每个consumer inst ...

  7. DevExpress v15.2新功能介绍视频(25集全)

    DevExpress v15.2新功能介绍视频(25集全) http://www.devexpresscn.com/Resources/Documentation-508.html DevExpres ...

  8. 【Linux】Ubuntu18.04镜像下载,新功能介绍

    一.Ubuntu18.04镜像下载 官方下载地址:http://releases.ubuntu.com/18.04/ 官方64位iso下载地址:http://releases.ubuntu.com/1 ...

  9. ORM 创新解放劳动力 -SqlSugar 新功能介绍

    介绍 SqlSugar是一款 老牌 .NET 开源ORM框架,由果糖大数据科技团队维护和更新 ,Github star数仅次于EF 和 Dapper 优点: 简单易用.功能齐全.高性能.轻量级.服务齐 ...

随机推荐

  1. spring mvc3中的addFlashAttribute方法

    spring mvc3中的addFlashAttribute方法  

  2. 14.1.3 Turning Off InnoDB 关掉InnoDB

    14.1.3 Turning Off InnoDB 关掉InnoDB: Oracle 推荐InnoDB 作为首选的存储引擎用于典型的数据库应用,从单用户的wikis到博客, 到高端应用把性能推到极限. ...

  3. Oracle Autonomous Transactions(自治事务)

    Oracle Autonomous Transactions Autonomous transactions allow you to leave the context of the calling ...

  4. 中介者模式 C++ 实现

    #include<iostream> #include<string> #include<vector> #include<cstdlib> using ...

  5. 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由

    原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...

  6. C++ 可以多个函数声明

    c/c++可以有多个函数声明,但实现只能有一个 例子: //file t_defs.h #ifndef _T_DEFS_H_ #define _T_DEFS_H_ void say(void); #e ...

  7. 获取ocx运行路径的另一种方法

    在InitInstance里边可以获取   1 2 3 4 5 6 7 8 9 10 11 12     if (bInit)     {         // TODO: 在此添加您自己的模块初始化 ...

  8. HDU4869:Turn the pokers(费马小定理+高速幂)

    Problem Description During summer vacation,Alice stay at home for a long time, with nothing to do. S ...

  9. Oracle heap 表的主键 dump 分析

    1. 创建heap 表: create table t1 (id char(10) primary key,a1 char(10),a2 char(10),a3 char(10)); SQL> ...

  10. Practical Common Lisp

    Practical Common Lisp Practical Common Lisp