diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e43b0f98 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/CNAME b/CNAME new file mode 100755 index 00000000..eb06dc03 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +www.swiftguide.cn \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 9117e11e..352c17a8 --- a/README.md +++ b/README.md @@ -11,78 +11,75 @@ 使用Gitbook制作,可以直接[在线阅读](http://numbbbbb.github.io/the-swift-programming-language-in-chinese/)。 +# 电子书下载 -# 翻译进度 +CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/1402621206) -> 说明:翻译之前请先到PR列表中查看别人认领的内容,尽量不要重复,谢谢! +其他格式可以通过PDF转换 + +# 当前阶段 + +文章已经全部翻译完成,当前阶段为自由校对阶段,可以随意提issue和pr。 + + +# 译者记录 * 欢迎使用 Swift - * 关于 Swift(完成 By numbbbbb) - * Swift 初见(完成 By numbbbbb) + * 关于 Swift ([numbbbbb]) + * Swift 初见 ([numbbbbb]) * Swift 教程 - * 基础部分(完成 By numbbbbb, lyuka, JaySurplus) - * 基本操作符(完成 By @xielingwang) - * 字符串和字符(完成 By @wh1100717) - * 集合类型(认领) - * 控制流(@vclwei, @coverxit, @NicePiao 认领) - * 函数(完成 By @honghaoz) - * 闭包(完成 By @wh1100717) - * 枚举(完成 By @yankuangshi) - * 类和结构体(@JaySurplus 认领) - * 属性(完成 By @shinyzhu) - * 方法(完成 By @pp-prog) - * 下标(完成 By @siemenliu) - * 继承(完成 By @Hawstein) - * 构造过程(@lifedim 认领) - * 析构过程(完成) - * 自动引用计数(完成 By @TimothyYe) - * 可选链(完成) - * 类型检查(完成 By @xiehurricane) - * 嵌套类型(完成 By @Lin-H) - * 扩展(完成 By @lyuka) - * 协议(完成 By @geek5nan) - * 泛型(完成 By @takalard) - * 高级操作符(@xielingwang 认领) + * 基础部分 ([numbbbbb], [lyuka], [JaySurplus]) + * 基本操作符 ([xielingwang]) + * 字符串和字符 ([wh1100717]) + * 集合类型 ([zqp]) + * 控制流 ([vclwei], [coverxit], [NicePiao]) + * 函数 ([honghaoz]) + * 闭包 ([wh1100717]) + * 枚举 ([yankuangshi]) + * 类和结构体 ([JaySurplus]) + * 属性 ([shinyzhu]) + * 方法 ([pp-prog]) + * 下标 ([siemenliu]) + * 继承 ([Hawstein]) + * 构造过程 ([lifedim]) + * 析构过程 ([bruce0505]) + * 自动引用计数 ([TimothyYe]) + * 可选链 ([Jasonbroker]) + * 类型检查 ([xiehurricane]) + * 嵌套类型 ([Lin-H]) + * 扩展 ([lyuka]) + * 协议 ([geek5nan]) + * 泛型 ([takalard]) + * 高级操作符 ([xielingwang]) * 语言参考 - * 关于语言参考(完成 By @ChildhoodAndy) - * 词法结构(完成 By @superkam) - * 类型(完成 By @lyuka) - * 表达式(@sg552 认领) - * 语句(完成 By @coverxit) - * 声明(@marsprince 认领) - * 特性(完成 By @Hawstein) - * 模式(@honghaoz 认领) - * 泛型参数(完成 By @fd5788) - * 语法总结(@StanZhai 认领) - -# 更新频率 - -由于我是利用业余时间翻译,所以速度有限。 - -不过我会保证每天至少1小时的翻译时间。 - + * 关于语言参考 ([dabing1022]) + * 词法结构 ([superkam]) + * 类型 ([lyuka]) + * 表达式 ([sg552] ) + * 语句 ([coverxit]) + * 声明 ([marsprince]) + * 特性 ([Hawstein]) + * 模式 ([honghaoz]) + * 泛型参数 ([fd5788]) + * 语法总结 ([StanZhai]) # 贡献力量 如果想做出贡献的话,你可以: -- 帮忙一起翻译 - 帮忙校对,挑错别字、病句等等 - 提出修改建议 - 提出术语翻译建议 # 翻译建议 -如果你愿意一起翻译的话,请仔细阅读: +如果你愿意一起校对的话,请仔细阅读: - 使用markdown进行翻译,文件名必须使用英文,因为中文的话gitbook编译的时候会出问题 - 翻译后的文档请放到source文件夹下的对应章节中,然后pull request即可,我会用gitbook编译成网页 - 工作分支为gh-pages,用于GitHub的pages服务 - fork过去之后新建一个分支进行翻译,完成后pull request这个分支,没问题的话我会合并到gh-pages分支中 - 有其他任何问题都欢迎发issue,我看到了会尽快回复 -- **尽早pull request**,你不必翻译完整篇文章再pr,完全可以翻译完一段就pr一次,这样别的朋友可以及时看到你的进度,避免多人翻译同一段。此外,尽早pr也可以让校对的朋友们更早看到新内容,更快发现问题 -- 一定要记得**先查看当前的pr**再开始翻译,防止重复翻译 -- 我已经提前把所有章节的markdown文件都创建好了,翻译的时候直接写入对应文件即可 谢谢! @@ -107,12 +104,71 @@ 7. 运行 `git fetch upstream gh-pages` 拉取我的库的更新到本地 8. 运行 `git rebase upstream/gh-pages` 将我的更新合并到你的分支 -这是一个初始化流程,只需要做一遍就行,之后请一直在develop分支进行翻译。 +这是一个初始化流程,只需要做一遍就行,之后请一直在develop分支进行修改。 -如果翻译过程中我的库有了更新,请重复6、7、8步。 +如果修改过程中我的库有了更新,请重复6、7、8步。 -翻译完成之后,首先push到你的库,然后登录GitHub,在你的库的首页可以看到一个 `pull request` 按钮,点击它,填写一些说明信息,然后提交即可。 +修改之后,首先push到你的库,然后登录GitHub,在你的库的首页可以看到一个 `pull request` 按钮,点击它,填写一些说明信息,然后提交即可。 # 开源协议 基于[WTFPL](http://en.wikipedia.org/wiki/WTFPL)协议开源。 + + + +[numbbbbb]:https://github.com/numbbbbb +[stanzhai]:https://github.com/stanzhai +[coverxit]:https://github.com/coverxit +[wh1100717]:https://github.com/wh1100717 +[TimothyYe]:https://github.com/TimothyYe +[honghaoz]:https://github.com/honghaoz +[lyuka]:https://github.com/lyuka +[JaySurplus]:https://github.com/JaySurplus +[Hawstein]:https://github.com/Hawstein +[geek5nan]:https://github.com/geek5nan +[yankuangshi]:https://github.com/yankuangshi +[xielingwang]:https://github.com/xielingwang +[yulingtianxia]:https://github.com/yulingtianxia +[twlkyao]:https://github.com/twlkyao +[dabing1022]:https://github.com/dabing1022 +[vclwei]:https://github.com/vclwei +[fd5788]:https://github.com/fd5788 +[siemenliu]:https://github.com/siemenliu +[youkugems]:https://github.com/youkugems +[haolloyin]:https://github.com/haolloyin +[wxstars]:https://github.com/wxstars +[IceskYsl]:https://github.com/IceskYsl +[sg552]:https://github.com/sg552 +[superkam]:https://github.com/superkam +[zac1st1k]:https://github.com/zac1st1k +[bzsy]:https://github.com/bzsy +[pyanfield]:https://github.com/pyanfield +[ericzyh]:https://github.com/ericzyh +[peiyucn]:https://github.com/peiyucn +[sunfiled]:https://github.com/sunfiled +[lzw120]:https://github.com/lzw120 +[viztor]:https://github.com/viztor +[wongzigii]:https://github.com/wongzigii +[umcsdon]:https://github.com/umcsdon +[zq54zquan]:https://github.com/zq54zquan +[xiehurricane]:https://github.com/xiehurricane +[Jasonbroker]:https://github.com/Jasonbroker +[tualatrix]:https://github.com/tualatrix +[pp-prog]:https://github.com/pp-prog +[088haizi]:https://github.com/088haizi +[baocaixiong]:https://github.com/baocaixiong +[yeahdongcn]:https://github.com/yeahdongcn +[shinyzhu]:https://github.com/shinyzhu +[lslxdx]:https://github.com/lslxdx +[Evilcome]:https://github.com/Evilcome +[zqp]:https://github.com/zqp +[NicePiao]:https://github.com/NicePiao +[LunaticM]:https://github.com/LunaticM +[menlongsheng]:https://github.com/menlongsheng +[lifedim]:https://github.com/lifedim +[happyming]:https://github.com/happyming +[bruce0505]:https://github.com/bruce0505 +[Lin-H]:https://github.com/Lin-H +[takalard]:https://github.com/takalard +[dabing1022]:https://github.com/dabing1022 +[marsprince]:https://github.com/marsprince \ No newline at end of file diff --git a/Using Swift with Cocoa and ObjectiveC/02Interoperability/01Interacting with Objective-C APIs.md b/Using Swift with Cocoa and ObjectiveC/02Interoperability/01Interacting with Objective-C APIs.md old mode 100644 new mode 100755 index fca0d8c9..8bbaa431 --- a/Using Swift with Cocoa and ObjectiveC/02Interoperability/01Interacting with Objective-C APIs.md +++ b/Using Swift with Cocoa and ObjectiveC/02Interoperability/01Interacting with Objective-C APIs.md @@ -1,318 +1,62 @@ ---- [@haolloyin](https://github.com/haolloyin) 翻译自苹果官方文档 [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_75) 的 [Interacting with Objective-C APIs](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html) 章节 +# 个人信息 +梁杰,男,北京航空航天大学,大三。 -## Interacting with Objective-C APIs - 与 Objc API 交互 +热爱Python,喜欢前端,GitHub重度脑残粉,目前正在维护swiftist.org社区。 -Swift 和 Objc 之间双向的`互用性`(`Interoperability`),让你可以用一种语言访问另一种语言写的代码。当你开始整合 Swift 代码到应用的开发工作流时,可以很好地理解这种互用性在重新定义,改善并强化你写 Cocoa 应用的方式。 +# 时间点 -互用性一个很重要的方面是在写 Swift 代码的同时可以用 Objc API。导入 Objc framework 之后,你可以用 Swift 原生的语法来实例化类,并和它们进行交互。 +6.3 项目发布 第一天仅有50Star +6.4 开始有人关注 300+Star +6.5~6.6 翻译工作开始步入正轨 +6.7~6.8 翻译速度一般 +6.9~6.11 建立QQ群 翻译速度加快 完成翻译 初步校对 +6.12 发布 -### Initialization - 初始化 +# 发起原因 -你可以用 Swift 语法调用 Objc 类的初始化方法,在 Swift 代码中进行 Objc 类的实例化。因为 Objc 的初始化方法都迁移到 Swift 了。`init` 前缀被去掉并成为关键字表示一个方法是初始化方法。以 `initWith` 开头的初始化方法中的 `With` 也被去掉了。从 `init` 或 `initWith` 中切分出来首字母变成小写,并且作为第一个参数名,该 selector 的其他部分也同样变成参数名。selector 括号内的各部分也是调用端 +最初其实没想到会做成这样,只是想着既然Swift这么火,我也想学一学,不如用点心去翻译一下,让我们广大群众也懂得一些知识,也算是为大家做点贡献。 -例如 Objc 中这么写: +万万没想到,最后变成了一个这么大的开源协作项目。 -``` -// OBJECTIVE-C +# 协作形式 -UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; -``` +通过GitHub进行协作,文章使用markdown写成,用gitbook制作成静态页面并托管在GitHub上,可以直接在线阅读。markdown也可以转换成Epub、PDF、mobi等多种电子书格式。 -Swift 中则这么写: +参与翻译的朋友只需要更新markdown文件内容即可,我会将内容通过gitbook转换成页面并更新到GitHub。 -``` -// SWIFT +# 如何吸引译者 -let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped) -``` +项目发起之后我只是在自己的微博上提了一下,开始时候并没有什么人关注,不过经过一些大号转发之后关注的人越来越多,就开始有人参与进来。 -不需要调用 `alloc`,Swift 会为你正确处理。**注意调用 Swift 初始化方法时不应该再出现 `init` 字眼。** +其实能吸引到这么多人,主要还是因为苹果的影响力太大,再加上我发起项目的时间非常早,正是全民Swift的时候,所以吸引了很多人参与。 -初始化时你可以显式指明变量的类型,也可以忽略不写,Swift 的类型推导会确定对象的类型。 +# 如何组织开源翻译 -``` -// SWIFT +## 让新手也能参与 -let myTextField = UITextField(frame: CGRect(0.0, 0.0, 200.0, 40.0)) -``` +GitHub在国内的普及程度还是不够,很多有兴趣参与的朋友都不太会用。刚开始我也没有意识到这个问题,直到有一个朋友主动问我我才明白过来,迅速在项目首页的说明中添加了详细的贡献代码教程。实践证明很多朋友都是照着这个教程完成了工作。 -`UITableView` 和 `UITextField` 拥有和 Objc 类似的方法,可以像在 Objc 代码中那样使用它们来访问在类中定义的属性或方法。 +## 传达信息 -为了一致性和简单性,Objc 的工厂方法在 Swift 中以方便的初始化方法出现,这种映射方法使它们和初始化方法一样简洁明了。例如你在 Objc 中这么调用工厂方法: +组织开源项目最重要的一点就是保证信息的传达,其实秘诀很简单——重复说。 -``` -// OBJECTIVE-C +就拿 Swift 这本书来举例,我一直在项目说明中更新当前进度,按理说大家点进来都会立刻看到,但是仍然有很多朋友问我现在翻译了多少、还有没有未认领章节。之后我就开始主动通知大家,在所有能通知的地方通知,一旦有新变动就马上通知,慢慢的就没有人问我了,因为大家都很清楚项目进度。 -UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0]; -``` +重要的信息比如时间节点,一定要多次强调。刚开始的一段时间虽然章节很快被认领,但是完成的人很少。后来我开始在群里说,周三完成翻译开始校对,一天说了有十几遍吧,然后从第二天开始完成的人就越来越多。 -在 Swift 中这么调用: +大家参与开源项目时相对来说是比较被动的,如果你希望控制时间的话,一定要多次强调,把这个信息发送到每个人的潜意识里。 -``` -// SWIFT +## 把握发展方向 -let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0) -``` +很多人会参与进来,但是几乎没人会主动考虑这个项目该如何发展,一定要记住这一点。 -### Accessing Properties - 属性访问 +如果你觉得很多人参与进来你就可以休息的话,那就大错特错了,大家擅长帮忙,但并不擅长主导项目。所以你要时刻提醒自己,下一步的目标是什么?我们应该怎么去做?主动提出一个方案然后和大家讨论,千万不要提出一个问题然后等待答案。 -在 Swift 中用点语法来访问、设置属性。 - -``` -// SWIFT - -myTextField.textColor = UIColor.darkGrayColor() -myTextField.text = "Hello world" -if myTextField.editing { - myTextField.editing = false -} -``` - -当获取或设置属性时,直接用属性名,不需要括号。注意 `darkGrayColor` 是 `UIColor` 中的方法,不是属性,因此带有括号。 - -在 Objc 中返回一个值并且不带参数的方法,可以看作隐式的属性访问器,并用和访问属性一样的语法来调用。这在 Swift 中很简单,只有在 Objc 中用 `@property` 语法声明的属性才会被看作属性。方法的导入、调用详见 [Working with Methods](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_29)。 - -### Working with Methods - 方法调用 - -在 Swift 中调用 Obc 方法时,使用点语法。 - -因为 Objc 的方法都迁移到 Swift,方法名的第一部分作为基本的方法名,并出现在小括号左边。第一个实参写在小括号里面,并且不需要参数名;方法名的其余部分与实参相应写在小括号里面。方法名的所有部分在调用端都是必须的。 - -例如 Objc 中这样调用: - -``` -// OBJECTIVE-C - -[myTableView insertSubview:mySubview atIndex:2]; -``` - -在 Swift 中这样调用: - -``` -// SWIFT - -myTableView.insertSubview(mySubview, atIndex: 2) -``` - -如果调用的方法不带参数,你还是需要写上括号。 - -``` -// SWIFT - -myTableView.layoutIfNeeded() -``` - -### id Compatibility - 兼容 id - -Swift 包含一个叫做 `AnyObject` 的协议类型来表示任意一种对象,就像 Objc `id` 一样。`AnyObject` 协议允许你在写类型安全的 Swift 代码的同时,保留无类型(`untyped`)对象的灵活性。正由于 `AnyObjcet` 协议提供的安全性,Swift 用 `AnyObject` 替代 `id`。 - -像 `id` 一样,你可以把任何类类型的对象赋值(assign)给用 `AnyObject` 声明的常量或变量,也可以把变量重新赋值到不同类型的对象。 - -``` -// SWIFT - -var myObject: AnyObject = UITableViewCell() -myObject = NSDate() -``` - -也可以不经过类型转换来调用 Objc 的方法或访问属性(后赋值给常量),但必须是 Objc 中用 `@objc attribute` 标记过的兼容方法。 - -``` -// SWIFT - -let futureDate = myObject.dateByAddingTimeInterval(10) -let timeSinceNow = myObject.timeIntervalSinceNow -``` - -然而因为 `AnyObject` 对象只有到运行时才确定其真实类型,这很可能写出不安全的代码。另外对比 Objc ,如果你调用(或访问) `AnyObject` 对象不存在的方法(或属性),这会导致运行时错误。例如下面的代码会编译通过,但是在运行时引发 `unrecognized selector error`: - -``` -// SWIFT - -myObject.characterAtIndex(5) -// crash, myObject does't respond to that method -``` - -但是,你可以在代码中用 Swift 中的`可选值`(`optional`)来消除这种常见的 Objc error。当你在一个 `AnyObject` 类型的对象上调用一个 Objc 方法时,事实上跟`可选值隐式拆包`(`implicitly unwrapped optional`)很类似。你可以用跟可选协议中的方法相同的`可选链`(`optional chaining`)语法来调用 `AnyObject` 对象的方法。这对属性也同样适用。 - -例如下面的代码,第1~2行将不会执行,因为 `NSDate` 对象不存在 `length` 属性和 `characterAtIndex:` 方法。常量 `myLength` 将会被推断为可选整型(`optional Int`),并且赋值为 `nil`。你也可以用 `if-let` 语句来尝试性地拆包方法调用的结果,因为对象调用的方法可能不存在(not respond to),正如下面第3行所示: - -``` -// SWIFT - -let myLength = myObject.length? -let myChar = myObject.characterAtIndex?(5) -if let fifthCharacter = myObject.characterAtIndex(5) { - println("Found \(fifthCharacter) at index 5") -} -``` - -> 译注:关于可选链(`Optional Chain`),建议读下官方教程的 [Optional Chaining](https://github.com/CocoaChina-editors/Welcome-to-Swift/blob/master/The%20Swift%20Programming%20Language/02Language%20Guide/17Optional%20Chaining.md) 和这篇 “[Swift之 ? 和 !](http://joeyio.com/ios/2014/06/04/swift---/)”。 - -和 Swfit 中所有向下转型(`downcast`)一样,从 `AnyObject` 转型为具体对象类型是不保证成功的,因此会返回可选值(`optional value`),你可以通过检测可选值来确定转型是否成功。 - -``` -// SWIFT - -let userDefaults = NSUserDefaults.standardUserDefaults() -let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate") -if let date = lastRefreshDate as? NSDate { - println("\(date.timeIntervalSinceReferenceDate)") -} -``` - -Of course, if you are certain of the type of the object (and know that it is not nil), you can force the invocation with the as operator. - -当然如果你确定一个对象的类型(并且对象不为 `nil`),你可以把它作为操作数强行调用。 - -``` -// SWIFT - -let myDate = lastRefreshDate as NSDate -let timeInterval = myDate.timeIntervalSinceReferenceDate -``` - -### Working with nil - 关于 nil - -Objc 用原始指针(可能为 `NULL`,即 Objc 中的 `nil`)来指向对象。Swift 中的所有值(包括结构体,对象引用)都是非空的(`non-nil`)。作为替代,可以用一个`可选类型`(`optional type`)来表示一个值,尽管在封包过程中可能会丢失这个值。当值丢失时,得到的是 `nil`。阅读 [Optional](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5) 来了解更多。 - -**由于 Objc 没有确保对象非空(`non-nil`),Swift 在将 Objc API 导入时,将所有实参类型和返回类型都改成可选类型。当你使用 Objc 对象时,应该先检查它是否为 `nil`。** - -在某些情况下,你可能100%确定一个 Objc 方法或属性不会返回 `nil` 对象引用。为了在这些情景下更方便地使用对象,Swift 引入了叫做`隐式拆包可选值`(`implicitly unwrapped optionals`)的类型,它包含所有可选类型的安全特性。另外,你可以直接访问它的值,不用判断是否为 `nil`或者将它拆包。当你在未经过安全拆包之前访问这种可选类型的值时,隐式拆包可选值会检查值是否已经丢失,如果已经丢失,会发生运行时错误。因此,你应该总是自行检查或拆包一个`隐式拆包可选值`,除非你确定这个值没有丢失。 - - -### Extensions - 扩展 - -Swift 的扩展(`extension`)类似于 Objc 的类别(`category`),`extension` 给 Swift 现有的类,结构体,枚举,增加行为,也适用于 Objc 中定义的类、结构体和枚举。你可以给一个类型定义一个 `extension`,不管这个类型来自系统 framework 还是你自定义的类型。简单地导入对应的模块,用你在 Objc 中那样用相同的名字引用类,结构体和枚举。 - -例如你可以用等边三角型来扩展 `UIBezierPath` 类,基于你提供的边长和起始点创建一个简单的 Bézier 路径。 - -``` -// SWIFT - -extension UIBezierPath { - convenience init(triangleSideLength: Float, origin: CGPoint) { - self.init() - let squareRoot = Float(sqrt(3)) - let altitude = (squareRoot * triangleSideLength) / 2 - moveToPoint(origin) - addLineToPoint(CGPoint(triangleSideLength, origin.x)) - addLineToPoint(CGPoint(triangleSideLength / 2, altitude)) - closePath() - } -} -``` - -可以用 `extension` 添加属性(包括类属性,静态属性),但是这些**属性必须进行计算;`extension` 并不能给类型,结构体,枚举添加存储属性(`stored property`)。** - -下面的例子扩展了 `CGRect` 结构体,使之拥有一个计算 `area` 的属性: - -``` -// SWIFT - -extension CGRect { - var area: CGFloat { - return width * height - } -} -let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0) -let area = rect.area -// area: CGFloat = 500.0 -``` - -也可以用 `extension` 给类添加协议(`protocol conformance`)而无需继承这个类。如果是 Swift 中定义的协议,你可以将它添加到结构体或者枚举,不管这个结构体或枚举是来自 Swift 还是 Objc。 - -**不能用 `extension` 对 Objc 类型现有的方法或属性进行重载(`override`)。** - -### Closures - 闭包 - -Objective-C blocks are automatically imported as Swift closures. For example, here is an Objective-C block variable: - -Objc 的 `block` 被自动导入为 Swift 的闭包(`closure`)。例如下面的 Objc block 变量: - -``` -// OBJECTIVE-C - -void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) { - /* ... */ -} -``` - -在 Swift 中看起来是这样的: - -``` -// SWIFT - -let completionBlock: (NSData, NSError) -> Void = { data, error in - /* ... */ -} -``` - -Swift 的闭包和 Objc 的 block 是兼容的,所以你可以将 Swift 的闭包作为实参传给 Objc 中期望传入 block 的方法。Swift 的闭包和函数是相同的类型,所以你甚至可以传递 Swift 的函数名。 - -闭包拥有和 block 相似的捕获语义(`capture semantic`),但是有一个关键的不同之处:变量是可变的而不是拷贝一个副本。换句话说,Swift 中闭包的变量默认等同于 Objc 中用 `__block` 修饰的变量。 - -### Object Comparison - 对象比较 - -Swift 中比较两个对象有两种不同的方式。一种是相等(`equality ==`),比较两个对象的内容;另一种是全等(`identity ===`),比较两个常量或变量是否指向同一个的对象实例。 - -Swift 和 Objc 对象在 Swift 中一般用 `==` 和 `===` 操作符进行比较。Swift 为继承自 `NSObject` 类的对象提供了 `==` 操作符的默认实现,即 Swift 会调用 `NSObject` 类定义的 `isEqual:` 方法。`NSObject` 类只会判断是否全等(`identity comparison`,即是否指向同一实例),所以你应该自己实现 `NSObject` 子类的 `isEqual:` 方法。由于你可以传递 Swift 对心爱难过(包括那些没有继承自 `NSObject` 类的类对象)给 Objc API,你应该实现 `isEqual:` 方法,以便 Objc API 可以判断两个对象的内容是否相同,而不是判断是否指向同一个实例。 - -作为类对象判等的一部分,确保根据对象比较的规则来实现 `hash` 属性。进一步说,如果你想用你的类对象作为字典的 key,你还要实现 `Hashable` 协议的 `hashValue` 属性。 - -### Swift Type Compatibility - Swift 类型兼容性 - -当你在 Swift 中定义一个类继承自 `NSObject` 类或其他任意 Objc 类时,这个类自动与 Objc 兼容。这些步骤由 Swift 编译器为你完成。如果你不打算将一个 Swift 类导入到 Objc,那么你不用担心类型兼容相关的问题。另外,如果你的 Swift 类没有继承自 Objc 的类,并且你将会在 Objc 代码中使用,那么可以用 `@objc` 来修饰它。 - -`@objc attribute` 使你的 Swift API 在 Objc 与其运行时中可用。换句话说,你可以用 `@objc` 来修饰任何你想在 Objc 使用的 Swift 类,方法,属性。如果你的 Swift 类继承自 Objc 的类,编译器会自动给 Swift 类插入 `@objc`。编译器也会自动为类的所有方法和属性添加 `@objc`,只要这个类用 `@objc` 修饰了。当你用 `@IBOutlet` `@IBAction` `@NSManaged` 时,`@objc` 也会被自动加上。`@objc` 在你用 Objc 类的 `selector` 实现 `target-action` 设计模式这一类工作时很有用,例如 `NSTimer` 和 `UIButton`。 - -当你在 Objc 中使用 Swift API,编译器通常直接翻译。例如 Swift API `func playSong(name: String)` 会被导入 Objc 变成 `- (void)playSong:(NSString *)name`。但是有一个例外:当你在 Objc 中使用 Swift 初始化方法时,编译器为你在方法名最前面添加 `initWith` 字样,并且适当地将原来初始化方法的首字母大写。例如,Swift 的初始化方法 ` init (songName: String, artist: String)` 会被导入 Objc 变成 `(instancetype)initWithSongName:(NSString *)songName artist:(NSString *)artist`。 - -Swift 也提供一个 `@objc` 的变型类允许你指定 Objc 的符号名(`symbol`)。例如,如果你的 Swift 类名含有 Objc 不支持的字符,你可以指定一个替代名以便在 Objc 中使用。如果你想为 Swift 函数提供一个 Objc 名称,应该使用 Objc 的 `selector` 语法,要记得为 `selector` 的每一部分加上分号(`:`)。 - -``` -// SWIFT - -@objc(Squirrel) -class Белка { - @objc(initWithName:) - init (имя: String) { /*...*/ } - @objc(hideNuts:inTree:) - func прячьОрехи(Int, вДереве: Дерево) { /*...*/ } -} -``` - -当你给 Swift 类使用 `@objc(<#name#>)` 时,这个类可以在 Objc 中使用,并且不需要任何命名空间。因此 `@objc(<#name#>)` 在你迁移可存档的(`archivable`) Objc 类到 Swift 时很有用,因为被存档的(`archived`)对象在存档中保存了它们的类名,你在 Objc 类中应该用 `@objc(<#name#>)` 来指定同样的名字,使得旧的存档可以在你新的 Swift 类中反存档(`unarchived`)。 - -### Objective-C Selectors - -Objc 的 `selector` 是一种指向 Objc 方法名的类型。在 Swift 中,Objc 的 `selector` 相应地用 `Selector` 结构体表示。你可以用 string 字面量来构造一个 Swift 的 `Selector`,例如:`let mySelector: Selector = "tappedButton:"`。由于 string 字面量会自动转化为 `selector`,因此你可以传递一个 string 字面量给任何接受 `selector` 作为参数的方法。 - -``` -// SWIFT - -import UIKit - -class MyViewController: UIViewController { - let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50)) - - init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) { - super.init(nibName: nibName, bundle: nibBundle) - myButton.targetForAction("tappedButton:", withSender: self) - } - - func tappedButton(sender: UIButton!) { - println("tapped button") - } -} -``` - -> **注意** -> -> **`performSelector:` 方法和其他与方法调用(`selector-invoking`)相关的方法没有被迁移到 Swift,因为他们固有的不安全性。** - -如果你的 Swift 类继承自 Objc 的类,那么这个类的所有方法和属性都是对 Objc `selector` 可见的。反之,如果 Swift 类没有继承自 Objc 类,你需要加上 `@objc` 修饰符,使得它们成为 Objc 中可用的 `selector`,详见前面的 [Swift Type Compatibility](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_36) 一节。 +# 一点感想 +还是那句话,万万没想到。 +第一天我还在和朋友说,我真羡慕别人的项目,有200多个Star,结果第二天我自己的项目就有了300多个Star,第三天600多……开始时候其实是抱着“做做试试”的心态来翻译,但是当Star和译者多起来之后,翻译完成就变成了一个责任,你肩负的是所有人的努力,一定不能让大家失望。 +这大概是我21年来做得最大的一件事。 diff --git a/Using Swift with Cocoa and ObjectiveC/03Mix and Match/DAG_2x.png b/Using Swift with Cocoa and ObjectiveC/03Mix and Match/DAG_2x.png old mode 100644 new mode 100755 diff --git a/Using Swift with Cocoa and ObjectiveC/03Mix and Match/Swift and Objective-C in the Same Project.md b/Using Swift with Cocoa and ObjectiveC/03Mix and Match/Swift and Objective-C in the Same Project.md old mode 100644 new mode 100755 diff --git a/Using Swift with Cocoa and ObjectiveC/03Mix and Match/bridgingheader_2x.png b/Using Swift with Cocoa and ObjectiveC/03Mix and Match/bridgingheader_2x.png old mode 100644 new mode 100755 diff --git a/change_cdn.py b/change_cdn.py old mode 100644 new mode 100755 index 312074b9..9842e4c7 --- a/change_cdn.py +++ b/change_cdn.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# coding:utf-8 import os @@ -14,6 +15,7 @@ def iter(path): insert_pos = content.find("", content.find("Generated using GitBook")) + 6 content = content[:insert_pos] + '''
翻译无任何商业目的,仅供内部学习交流使用!
Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用。Swift 结合了 C 和 Objective-C 的优点并且不受C的兼容性的限制。Swift 使用安全的编程模式并添加了很多新特性,这将使编程更简单,扩展性更强,也更有趣。除此之外,Swift 还支持人见人爱的 Cocoa 和 Cocoa Touch 框架。拥有了这些特性,Swift将重新定义软件开发。
-Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。得益于这些基础工作,我们现在可以发布一个新语言,用于未来的苹果软件的开发。
-Objective-C 开发者对于 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。
+++翻译:numbbbbb
+
校对:yeahdongcn
Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。
+Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
+Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
-Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 即可以用于开发“hello, world”这样的小程序,也可以用于开发一个完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。
-用 Swift 编写 iOS 和 OS X 应用将是一场美妙的体验,Swift 之后也会不断开发新特性和兼容性。我们对 Swift 充满信心,你还在等什么!
+Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。
+Swift 是编写 iOS 和 OS X 应用的极佳手段,并将伴随着新的特性和功能持续演进。我们对 Swift 充满信心,你还在等什么!
翻译无任何商业目的,仅供内部学习交流使用!
+ ++
本页内容包括:
通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
-println("Hello, world")
-如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要main函数。你同样不需要在每个语句结尾写上分号。
println("Hello, world")
+
+如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要main函数。你同样不需要在每个语句结尾写上分号。
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
-+注意:为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。
+注意: +为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。 +打开Playground
使用let来声明常量,使用var来声明变量。一个常量的值在编译时并不需要获取,但是你只能为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。
var myVariable = 42
+使用let来声明常量,使用var来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。
+var myVariable = 42
myVariable = 50
let myConstant = 42
-
常量或者变量的类型必须和你赋给它们的值一样。然而,声明时类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出myVariable是一个整数(integer)因为它的初始值是整数。
+
+常量或者变量的类型必须和你赋给它们的值一样。然而,声明时类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出myVariable是一个整数(integer)因为它的初始值是整数。
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
-let implicitInteger = 70
+let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
-
-练习:创建一个常量,显式指定类型为Float并指定初始值为4。
+
++练习: +创建一个常量,显式指定类型为
Float并指定初始值为4。
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
-let label = "The width is"
+let label = "The width is"
let width = 94
let widthLabel = label + String(width)
-
-练习:删除最后一行中的String,错误提示是什么?
+
++练习: +删除最后一行中的
String,错误提示是什么?
有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠。例如:
-let apples = 3
+let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
-
-练习:使用\()来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
+
++练习: +使用
\()来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用方括号[]来创建数组和字典,并使用下标或者键(key)来访问元素。
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
+var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
-
-var occupations = [
+
+var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
-
要创建一个空数组或者字典,使用初始化语法。
-let emptyArray = String[]()
+
+要创建一个空数组或者字典,使用初始化语法。
+let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()
-
如果类型信息可以被推断出来,你可以用[]和[:]来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样。
-shoppingList = [] // 去逛街并买点东西
-
控制流
+
+如果类型信息可以被推断出来,你可以用[]和[:]来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样。
shoppingList = [] // 去逛街并买点东西
+
+
+使用if和switch来进行条件操作,使用for-in、for、while和do-while来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
let individualScores = [75, 43, 103, 87, 12]
+let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
@@ -661,9 +689,10 @@ for score in individualScores {
}
}
teamScore
-
在if语句中,条件必须是一个布尔表达式——这意味着像if score { ... }这样的代码将报错,而不会隐形地与 0 做对比。
+
+在if语句中,条件必须是一个布尔表达式——这意味着像if score { ... }这样的代码将报错,而不会隐形地与 0 做对比。
你可以一起使用if和let来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是nil,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。
var optionalString: String? = "Hello"
+var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
@@ -671,12 +700,14 @@ var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
-
-练习:把optionalName改成nil,greeting会是什么?添加一个else语句,当optionalName是nil时给greeting赋一个不同的值。
+
++练习: +把
optionalName改成nil,greeting会是什么?添加一个else语句,当optionalName是nil时给greeting赋一个不同的值。
如果变量的可选值是nil,条件会判断为false,大括号中的代码会被跳过。如果不是nil,会将值赋给let后面的常量,这样代码块中就可以使用这个值了。
switch支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
let vegetable = "red pepper"
+let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
@@ -687,12 +718,14 @@ case let x where x.hasSuffix("pepper"):
default:
let vegetableComment = "Everything tastes good in soup."
}
-
-练习:删除default语句,看看会有什么错误?
+
++练习: +删除
default语句,看看会有什么错误?
运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break。
你可以使用for-in来遍历字典,需要两个变量来表示每个键值对。
let interestingNumbers = [
+let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
@@ -706,11 +739,13 @@ for (kind, numbers) in interestingNumbers {
}
}
largest
-
-练习:添加另一个变量来记录哪种类型的数字是最大的。
+
++练习: +添加另一个变量来记录哪种类型的数字是最大的。
使用while来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。
var n = 2
+var n = 2
while n < 100 {
n = n * 2
}
@@ -721,8 +756,9 @@ do {
m = m * 2
} while m < 100
m
-
你可以在循环中使用..来表示范围,也可以使用传统的写法,两者是等价的:
-var firstForLoop = 0
+
+你可以在循环中使用..来表示范围,也可以使用传统的写法,两者是等价的:
+var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
@@ -733,23 +769,28 @@ for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop
-
使用..创建的范围不包含上界,如果想包含的话需要使用...。
+
+使用..创建的范围不包含上界,如果想包含的话需要使用...。
使用func来声明一个函数,使用名字和参数来调用函数。使用->来指定函数返回值。
func greet(name: String, day: String) -> String {
+func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
-
-练习:删除day参数,添加一个参数来表示今天吃了什么午饭。
+
++练习: +删除
day参数,添加一个参数来表示今天吃了什么午饭。
使用一个元组来返回多个值。
-func getGasPrices() -> (Double, Double, Double) {
+func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
getGasPrices()
-
函数的参数数量是可变的,用一个数组来获取它们:
-func sumOf(numbers: Int...) -> Int {
+
+函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式:
+func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
@@ -758,11 +799,13 @@ getGasPrices()
}
sumOf()
sumOf(42, 597, 12)
-
-练习:写一个计算参数平均值的函数。
+
++练习: +写一个计算参数平均值的函数。
函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,你可以使用嵌套函数来重构一个太长或者太复杂的函数。
-func returnFifteen() -> Int {
+func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
@@ -771,8 +814,9 @@ sumOf(42, 597, 12)
return y
}
returnFifteen()
-
函数是一等公民,这意味着函数可以作为另一个函数的返回值。
-func makeIncrementer() -> (Int -> Int) {
+
+函数是第一等类型,这意味着函数可以作为另一个函数的返回值。
+func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
@@ -780,8 +824,9 @@ returnFifteen()
}
var increment = makeIncrementer()
increment(7)
-
函数也可以当做参数传入另一个函数。
-func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
+
+函数也可以当做参数传入另一个函数。
+func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
@@ -794,35 +839,45 @@ func lessThanTen(number: Int) -> Bool {
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)
-
函数实际上是一种特殊的闭包,你可以使用{}来创建一个匿名闭包。使用in来分割参数、返回值与执行体。
-numbers.map({
+
+函数实际上是一种特殊的闭包,你可以使用{}来创建一个匿名闭包。使用in将参数和返回值类型声明与闭包函数体进行分离。
+numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
- })
-
-练习:重写闭包,对所有奇数返回0.
+})
+
++练习: +重写闭包,对所有奇数返回0。
有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
+numbers.map({ number in 3 * number })
+
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
-sort([1, 5, 3, 12, 2]) { $0 > $1 }
-sort([1, 5, 3, 12, 2]) { $0 > $1 }
+
+
+使用class和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。
class Shape {
+class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
-
-练习:使用let添加一个常量属性,再添加一个接收一个参数的方法。
+
++练习: +使用
let添加一个常量属性,再添加一个接收一个参数的方法。
要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。
-var shape = Shape()
+var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
-
这个版本的Shape类缺少了一些重要的东西:一个构造函数来初始化类实例。使用init来创建一个构造器。
-class NamedShape {
+
+这个版本的Shape类缺少了一些重要的东西:一个构造函数来初始化类实例。使用init来创建一个构造器。
+class NamedShape {
var numberOfSides: Int = 0
var name: String
@@ -834,11 +889,12 @@ var shapeDescription = shape.simpleDescription()
return "A shape with \(numberOfSides) sides."
}
}
-
注意self被用来区别实例变量。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像numberOfSides)还是通过构造器(就像name)。
+
+注意self被用来区别实例变量。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像numberOfSides)还是通过构造器(就像name)。
如果你需要在删除对象之前进行一些清理工作,使用deinit创建一个析构函数。
子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。创建类的时候并不需要一个标准的根类,所以你可以忽略父类。
子类如果要重写父类的方法的话,需要用override标记——如果没有添加override就重写父类方法的话编译器会报错。编译器同样会检测override标记的方法是否确实在父类中。
class Square: NamedShape {
+class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
@@ -858,11 +914,13 @@ var shapeDescription = shape.simpleDescription()
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
-
-练习:创建NamedShape的另一个子类Circle,构造器接收两个参数,一个是半径一个是名称,实现area和describe方法。
+
++练习: +创建
NamedShape的另一个子类Circle,构造器接收两个参数,一个是半径一个是名称,实现area和describe方法。
属性可以有 getter 和 setter 。
-class EquilateralTriangle: NamedShape {
+class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
@@ -876,7 +934,7 @@ test.simpleDescription()
return 3.0 * sideLength
}
set {
- sideLength = newValue / 3.0
+ sideLength = newValue / 3.0
}
}
@@ -888,16 +946,17 @@ var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle"
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
-
在perimeter的 setter 中,新值的名字是newValue。你可以在set之后显式的设置一个名字。
+
+在perimeter的 setter 中,新值的名字是newValue。你可以在set之后显式的设置一个名字。
注意EquilateralTriangle类的构造器执行了三步:
如果你不需要计算属性但是需要在设置一个新值之前运行一些代码,使用willSet和didSet。
如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSet和didSet。
比如,下面的类确保三角形的边长总是和正方形的边长相同。
-class TriangleAndSquare {
+class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
@@ -918,8 +977,9 @@ triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
-
类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。
-class Counter {
+
+类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。
+class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
@@ -927,12 +987,15 @@ triangleAndSquare.triangle.sideLength
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
-
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?。如果?之前的值是nil,?后面的东西都会被忽略,并且整个表达式返回nil。否则,?之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
-let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
+
+处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?。如果?之前的值是nil,?后面的东西都会被忽略,并且整个表达式返回nil。否则,?之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
+let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
-
枚举和结构体
+
+
+使用enum来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。
enum Rank: Int {
+enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
@@ -953,16 +1016,19 @@ let sideLength = optionalSquare?.sideLength
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
-
-练习:写一个函数,通过比较它们的原始值来比较两个Rank值。
+
++练习: +写一个函数,通过比较它们的原始值来比较两个
Rank值。
在上面的例子中,枚举原始值的类型是Int,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。
使用toRaw和fromRaw函数来在原始值和枚举值之间进行转换。
if let convertedRank = Rank.fromRaw(3) {
+if let convertedRank = Rank.fromRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}
-
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。
-enum Suit {
+
+枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。
+enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
@@ -980,13 +1046,15 @@ let aceRawValue = ace.toRaw()
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
-
-练习:给Suit添加一个color方法,对spades和clubs返回“black”,对hearts和diamonds返回“red”。
+
++练习: +给
Suit添加一个color方法,对spades和clubs返回“black”,对hearts和diamonds返回“red”。
注意,有两种方式可以引用Hearts成员:给hearts常量赋值时,枚举成员Suit.Hearts需要用全名来引用,因为常量没有显式指定类型。在switch里,枚举成员使用缩写.Hearts来引用,因为self的值已经知道是一个suit。已知变量类型的情况下你可以使用缩写。
使用struct来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是
结构体是传值,类是传引用。
struct Card {
+struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
@@ -996,12 +1064,14 @@ let heartsDescription = hearts.simpleDescription()
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
-
-练习:给Card添加一个方法,创建一副完整的扑克牌并把每张牌的rank和suit对应起来。
+
++练习: +给
Card添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来。
一个枚举成员的实例可以有实例值。相同枚举成员的实例可以有不同的值。创建实例的时候传入值即可。实例值和原始值是不同的:枚举成员的原始值对于所有实例都是相同的,而且你是在定义枚举的时候设置原始值。
例如,考虑从服务器获取日出和日落的时间。服务器会返回正常结果或者错误信息。
-enum ServerResponse {
+enum ServerResponse {
case Result(String, String)
case Error(String)
}
@@ -1015,18 +1085,22 @@ case let .Result(sunrise, sunset):
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
-
-练习:给ServerResponse和switch添加第三种情况。
+
++练习: +给
ServerResponse和switch添加第三种情况。
注意如何从ServerResponse中提取日升和日落时间。
使用protocol来声明一个接口。
protocol ExampleProtocol {
+
+协议和扩展
+使用protocol来声明一个协议。
+protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
-
类、枚举和结构体都可以实现接口。
-class SimpleClass: ExampleProtocol {
+
+类、枚举和结构体都可以实现协议。
+class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
@@ -1046,12 +1120,14 @@ struct SimpleStructure: ExampleProtocol {
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
-
-练习:写一个实现这个接口的枚举。
+
++练习: +写一个实现这个协议的枚举。
注意声明SimpleStructure时候mutating关键字用来标记一个会修改结构体的方法。SimpleClass的声明不需要标记任何方法因为类中的方法经常会修改类。
使用extension来为现有的类型添加功能,比如添加一个计算属性的方法。你可以使用扩展来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型。
extension Int: ExampleProtocol {
+使用extension来为现有的类型添加功能,比如新的方法和参数。你可以使用扩展来改造定义在别处,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
+extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
@@ -1060,17 +1136,21 @@ let bDescription = b.simpleDescription
}
}
7.simpleDescription
-
-练习:给Double类型写一个扩展,添加absoluteValue功能。
+
++-练习: +给
Double类型写一个扩展,添加absoluteValue功能。
你可以像使用其他命名类型一样使用接口名——例如,创建一个有不同类型但是都实现一个接口的对象集合。当你处理类型是接口的值时,接口外定义的方法不可用。
-let protocolValue: ExampleProtocol = a
+你可以像使用其他命名类型一样使用协议名——例如,创建一个有不同类型但是都实现一个协议的对象集合。当你处理类型是协议的值时,协议外定义的方法不可用。
+let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty // Uncomment to see the error
-
即使protocolValue变量运行时的类型是simpleClass,编译器会把它的类型当做ExampleProtocol。这表示你不能调用类在它实现的接口之外实现的方法或者属性。
+
+即使protocolValue变量运行时的类型是simpleClass,编译器会把它的类型当做ExampleProtocol。这表示你不能调用类在它实现的协议之外实现的方法或者属性。
在尖括号里写一个名字来创建一个泛型函数或者类型。
-func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
+func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
var result = ItemType[]()
for i in 0..times {
result += item
@@ -1078,16 +1158,18 @@ protocolValue.simpleDescription
return result
}
repeat("knock", 4)
-
你也可以创建泛型类、枚举和结构体。
-// Reimplement the Swift standard library's optional type
+
+你也可以创建泛型类、枚举和结构体。
+// Reimplement the Swift standard library's optional type
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
-
在类型名后面使用where来指定一个需求列表——例如,要限定实现一个协议的类型,需要限定两个类型要相同,或者限定一个类必须有一个特定的父类。
-func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
+
+在类型名后面使用where来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
+func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
@@ -1098,10 +1180,12 @@ possibleInteger = .Some(100)
return false
}
anyCommonElements([1, 2, 3], [3])
-
-练习:修改anyCommonElements函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
+
++-练习: +修改
anyCommonElements函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
简单起见,你可以忽略where,只在冒号后面写接口或者类名。<T: Equatable>和<T where T: Equatable>是等价的。
简单起见,你可以忽略where,只在冒号后面写协议或者类名。<T: Equatable>和<T where T: Equatable>是等价的。
翻译无任何商业目的,仅供内部学习交流使用!
在本章中您将了解 Swift 的特性和开发历史,并对 Swift 有一个初步的了解。
@@ -609,16 +617,8 @@翻译无任何商业目的,仅供内部学习交流使用!
++翻译:numbbbbb, lyuka, JaySurplus
+
校对:lslxdx
本页包含内容:
+Swift 是 iOS 和 OS X 应用开发的一门新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
-Swift 的类型是在 C 和 Objective-C 的基础上提出的,Int是整型;Double和Float是浮点型;Bool是布尔型;String是字符串。Swift 还有两个有用的集合类型,Array和Dictionary,详情参见集合类型(待添加链接)。
Swift 的类型是在 C 和 Objective-C 的基础上提出的,Int是整型;Double和Float是浮点型;Bool是布尔型;String是字符串。Swift 还有两个有用的集合类型,Array和Dictionary,请参考集合类型。
就像 C 语言一样,Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,值不可变的变量有着广泛的应用,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更好地表达你的意图。
除了我们熟悉的类型,Swift 还增加了 Objective-C 中没有的类型比如元组(Tuple)。元组可以让你创建或者传递一组数据,比如作为函数的返回值时,你可以用一个元组可以返回多个值。
Swift 还增加了可选(Optional)类型,用于处理值缺失的情况。可选表示“那儿有一个值,并且它等于 x ”或者“那儿没有值”。可选有点像在 Objective-C 中使用nil,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的nil指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。
Swift 是一个类型安全的语言,可选就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个String,类型安全会阻止你不小心传入一个Int。你可以在开发阶段尽早发现并修正错误。
常量和变量把一个名字(比如maximumNumberOfLoginAttempts或者welcomeMessage)和一个指定类型的值(比如数字10或者字符串Hello)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
常量和变量把一个名字(比如maximumNumberOfLoginAttempts或者welcomeMessage)和一个指定类型的值(比如数字10或者字符串"Hello")关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
常量和变量必须在使用前声明,用let来声明常量,用var来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
let maximumNumberOfLoginAttempts = 10
+let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
-
这两行代码可以被理解为
-:
-“声明一个名字是maximumNumberOfLoginAttempts的新常量,并给它一个值10。然后,声明一个名字是currentLoginAttempt的变量并将它的值初始化为0.”
+
+这两行代码可以被理解为:
+“声明一个名字是maximumNumberOfLoginAttempts的新常量,并给它一个值10。然后,声明一个名字是currentLoginAttempt的变量并将它的值初始化为0.”
在这个例子中,允许的最大尝试登录次数被声明为一个常量,因为这个值不会改变。当前尝试登录次数被声明为一个变量,因为每次尝试登录失败的时候都需要增加这个值。
你可以在一行中声明多个常量或者多个变量,用逗号隔开:
-var x = 0.0, y = 0.0, z = 0.0
--注意:如果你的代码中有不需要改变的值,请将它声明为常量。只将需要改变的值声明为变量。
++var x = 0.0, y = 0.0, z = 0.0 ++注意:
如果你的代码中有不需要改变的值,请使用let关键字将它声明为常量。只将需要改变的值声明为变量。类型标注
-当你声明常量或者变量的时候可以加上类型标注,说明常量或者变量中要存储的值的类型。如果要添加类型标注,在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
+当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
这个例子给
-welcomeMessage变量添加了类型标注,表示这个变量可以存储String类型的值:var welcomeMessage: String -声明中的冒号代表着“是...类型”,所以这行代码可以被理解为::
++var welcomeMessage: String +声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:
“声明一个类型为
String,名字为welcomeMessage的变量。”“类型为
String”的意思是“可以存储任意String类型的值。”-
welcomeMessage变量现在可以被设置成任意字符串:welcomeMessage = "Hello" --注意:一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,详情参见
+类型安全和类型推断(待添加链接)。在上面的例子中,没有给welcomeMessage赋初始值,所以添加了一个类型标注。+welcomeMessage = "Hello" ++注意:
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考类型安全和类型推断。在上面的例子中,没有给welcomeMessage赋初始值,所以变量welcomeMessage的类型是通过一个类型标注指定的,而不是通过初始值推断的。常量和变量的命名
-你可以用任何你喜欢的字符作为常量和变量名,包括Unicode字符:
-let π = 3.14159 - let 你好 = "你好世界" - let 🐶🐮 = "dogcow" -常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode码位,连线与制表符。尽管常量与变量名中可以包含数字,但是它们不能以数字打头。
-一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者以改变其存储的值为其他类型。同时,你也不能将常量与变量进行互转。
+你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符:
++let π = 3.14159 +let 你好 = "你好世界" +let 🐶🐮 = "dogcow" +常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。
+一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
-注意:如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字围住的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
+注意:
如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。你可以更改现有的变量值为其他同类型的值,在下面的例子中,
-friendlyWelcome的值从"Hello!"改为了"Bonjour!":var friendlyWelcome = "Hello!" - friendlyWelcome = "Bonjour!" - // friendlyWelcome is now "Bonjour!" -和变量不一样,常量的值一旦被确定以后就不能更改了。尝试这样做会在编译时报错:
-let languageName = "Swift" - languageName = "Swift++" - // this is a compile-time error - languageName cannot be changed -输出常量和变量
++var friendlyWelcome = "Hello!" +friendlyWelcome = "Bonjour!" +// friendlyWelcome 现在是 "Bonjour!" +与变量不同,常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错:
++let languageName = "Swift" +languageName = "Swift++" +// 这会报编译时错误 - languageName 不可改变 +输出常量和变量
你可以用
-println函数来输出当前常量或变量的值:println(friendlyWelcome) - // prints "Bonjour!" -+
println是一个用来输出的全局函数,输出的内容会在最后带换行。如果你用Xcode,println将会输出内容到“console”面板上。(另一种函数叫+println(friendlyWelcome) +// 输出 "Bonjour!" +
println是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode,println将会输出内容到“console”面板上。(另一种函数叫-
println函数输出传入的String值:println("This is a string") - // prints "This is a string" -像Cocoa里的
-NSLog函数一样,println函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。Swift用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift会用当前常量或变量的值替换这些占位符。将常量或变量名放入反斜杠符加一对圆括号中
-"\()":println("The current value of friendlyWelcome is \(friendlyWelcome)") - // prints "The current value of friendlyWelcome is Bonjour! --注意:字符串插值所有可用的选项在 字符串插值 这章中讲述。
++println("This is a string") +// 输出 "This is a string" +与 Cocoa 里的
+NSLog函数类似的是,println函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
++println("The current value of friendlyWelcome is \(friendlyWelcome)") +// 输出 "The current value of friendlyWelcome is Bonjour! ++-注意:
字符串插值所有可用的选项,请参考字符串插值。注释
+ +注释
请将你的代码中的非执行文本注释成提示或者笔记以方便你将来阅读。Swift 的编译器将会在编译代码时自动忽略掉注释部分。
-Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠作(//)为起始标记:
-// this is a comment -你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(/*),终止标记为一个星号后跟随单个正斜杠(*/):
-/* this is also a comment, -but written over multiple lines */ -与C 语言多行注释不同的是,Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:
-/* this is the start of the first multiline comment -/* this is the second, nested multiline comment */ -this is the end of the first multiline comment */ -通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。
+Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠(
+//)作为起始标记:+// 这是一个注释 +你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(
+/*),终止标记为一个星号后跟随单个正斜杠(*/):+/* 这是一个, +多行注释 */ +与 C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:
++/* 这是第一个多行注释的开头 +/* 这是第二个被嵌套的多行注释 */ +这是第一个多行注释的结尾 */ +通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。
+分号
-与其他大部分编程语言不同,Swift 并不强制要求你在每条语句的结尾处使用分号(;),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:
-let cat = "🐱"; println(cat) -// prints "🐱" -整数
-整数就是没有小数部分的数字,比如
-42和-23。整数可以是有符号(正、负、零)或者无符号(正、零)。Swift 提供了8、16、32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是
+UInt8,32位有符号整数类型是Int32。就像 Swift 的其他类型一样,整数类型采用大写命名法。与其他大部分编程语言不同,Swift 并不强制要求你在每条语句的结尾处使用分号(
+;),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:+ +let cat = "🐱"; println(cat) +// 输出 "🐱" +整数
+整数就是没有小数部分的数字,比如
+42和-23。整数可以是有符号(正、负、零)或者无符号(正、零)。Swift 提供了8,16,32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是
UInt8,32位有符号整数类型是Int32。就像 Swift 的其他类型一样,整数类型采用大写命名法。整数范围
你可以访问不同整数类型的
-min和max属性来获取对应类型的最大值和最小值:+let minValue = UInt8.min // minValue 为 0,是 UInt8 类型的最小值 +let minValue = UInt8.min // minValue 为 0,是 UInt8 类型的最小值 let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值 -Int
+Int
一般来说,你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型
Int,长度与当前平台的原生字长相同:
- 在32位平台上,
@@ -693,8 +739,9 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值Int和Int32长度相同。- 在64位平台上,
UInt和UInt64长度相同。-+注意:尽量不要使用
+UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int,即使你要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推测,详情参见类型安全和类型推测。注意:
尽量不要使用UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int,即使你要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考类型安全和类型推断。浮点数
浮点数是有小数部分的数字,比如
3.14159,0.1和-273.15。浮点类型比整数类型表示的范围更大,可以存储比
@@ -703,268 +750,310 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值Int类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:Float表示32位浮点数。精度要求不高的话可以使用此类型。--注意:
+Double精确度很高,至少有15位数字,而Float最少只有6位数字。选择哪个类型取决于你的代码需要处理的数字大小。注意:
Double精确度很高,至少有15位数字,而Float最少只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。类型安全和类型推测
-Swift 是一个类型安全的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个
-String,你绝对不可能不小心传进去一个Int。Swift 是类型安全的,会在编译你的代码时进行类型检查,如果遇到不匹配的类型会报错。这可以让你在开发的时候尽早发现并修复错误。
-当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用类型推测来选择合适的类型。有了类型推测,编译器可以在编译代码的时候自动推测出表达式的类型。原理很简单,判断你赋的值即可。
-因为有类型推测,和 C 或者 Objc 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
-当你声明常量或者变量并赋初值的时候类型推测非常有用。当你在声明常量或者变量的时候赋给它们一个原始值即可触发类型推测。(原始值就是会直接出现在你代码中的值,比如
-42和3.14159。)举个例子,如果你给一个新常量赋值
-42并且没有标明类型,Swift 可以推测出常量类型是Int,因为你给它赋的初值看起来很像一个整数:+let meaningOfLife = 42 + +类型安全和类型推断
+Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个
+String,你绝对不可能不小心传进去一个Int。由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
+当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
+因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
+当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如
+42和3.14159。)例如,如果你给一个新常量赋值
+42并且没有标明类型,Swift 可以推断出常量类型是Int,因为你给它赋的初始值看起来像一个整数:let meaningOfLife = 42 // meaningOfLife 会被推测为 Int 类型 -同理,如果你没有给浮点原始值标明类型,Swift 会推测你想要的是
-Double:+let pi = 3.14159 +同理,如果你没有给浮点字面量标明类型,Swift 会推断你想要的是
+Double:let pi = 3.14159 // pi 会被推测为 Double 类型 -当推测浮点数的类型时,Swift 总是会选择
-Double而不是Float。如果表达式中同时出现了整数和浮点数,会被推测为
-Double类型:+let anotherPi = 3 + 0.14159 +当推断浮点数的类型时,Swift 总是会选择
+Double而不是Float。如果表达式中同时出现了整数和浮点数,会被推断为
+Double类型:let anotherPi = 3 + 0.14159 // anotherPi 会被推测为 Double 类型 -原始值
-3没有显式声明类型,而表达式中出现了一个浮点原始值,所以表达式会被推测为Double类型。数值类原始值
-整数原始值可以被写作:
+原始值
+ +3没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为Double类型。数值型字面量
+整数字面量可以被写作:
-
- 一个十进制数,没有前缀
- 一个二进制数,前缀是
0b- 一个八进制数,前缀是
0o- 一个十六进制数,前缀是
0x下面的所有整数原始值的十进制值都是
-17:+let decimalInteger = 17 +下面的所有整数字面量的十进制值都是
+17:let decimalInteger = 17 let binaryInteger = 0b10001 // 二进制的17 let octalInteger = 0o21 // 八进制的17 -let hexadecimalInteger = 0x11 // 十六机制的17 -浮点原始值可以是十进制(没有前缀)或者是十六进制(前缀是
+let hexadecimalInteger = 0x11 // 十六进制的17 +0x)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点原始值还有一个可选的指数,在十进制浮点数中通过大写或者小写的e来指定,在十六进制浮点数中通过大写或者小写的p来指定。浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是
0x)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的指数(exponent),在十进制浮点数中通过大写或者小写的e来指定,在十六进制浮点数中通过大写或者小写的p来指定。如果一个十进制数的指数为
exp,那这个数相当于基数和10^exp的乘积:-
- 1.25e2 表示 1.25 × 10^2,等于 125.0。
-- 1.25e-2 表示 1.25 × 10^-2,等于 0.0125。
+- +
1.25e2表示 1.25 × 10^2,等于125.0。1.25e-2表示 1.25 × 10^-2,等于0.0125。如果一个十六进制数的指数为
exp,那这个数相当于基数和2^exp的乘积:-
-- 0xFp2 表示 15 × 2^2,等于 60.0。
-- 0xFp-2 表示 15 × 2^-2,等于 3.75。
+- +
0xFp2表示 15 × 2^2,等于60.0。0xFp-2表示 15 × 2^-2,等于3.75。下面的这些浮点原始值都等于十进制的
-12.1875:+ +let decimalDouble = 12.1875 +下面的这些浮点字面量都等于十进制的
+12.1875:let decimalDouble = 12.1875 let exponentDouble = 1.21875e1 let hexadecimalDouble = 0xC.3p0 -数值类原始值可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响原始值:
-+let paddedDouble = 000123.456 +数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
+let paddedDouble = 000123.456 let oneMillion = 1_000_000 let justOverOneMillion = 1_000_000.000_000_1 -数值类型转换
-通常来讲,即使代码中的整数常量和变量已知非负,也请使用
Int类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类原始值的类型推测。 +数值型类型转换
+通常来讲,即使代码中的整数常量和变量已知非负,也请使用
Int类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。 只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。整数转换
-不同整数类型的变量和常量可以存储不同大小的数字。
-Int8类型的常量或者变量可以存储的数字范围是-128~127,UInt8类型的常量或者变量能存储的数字范围是0~255。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:+let cannotBeNegative: UInt8 = -1 +不同整数类型的变量和常量可以存储不同范围的数字。
+Int8类型的常量或者变量可以存储的数字范围是-128~127,而UInt8类型的常量或者变量能存储的数字范围是0~255。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:let cannotBeNegative: UInt8 = -1 // UInt8 类型不能存储负数,所以会报错 let tooBig: Int8 = Int8.max + 1 // Int8 类型不能存储超过最大值的数,所以会报错 -因为每一个整数类型都可以存储不同范围的值,你必须根据情况来选择不同的转换方法。不同的转换方法可以暴露出隐藏的转换错误并让你的代码更加清晰。
-要将一种数字类型转换成另一种,你要用当前值来初始化一个新数字,这个数字的类型就是你的目标类型。在下面的例子中,常量
-twoThousand类型是UInt16,然而常量one类型是Uint8。它们不能直接相加,因为它们类型不同。所以要调用UInt16(one)来创建一个新的UInt16数字并用one的值来初始化,然后使用这个新数字来计算:+let twoThousand: UInt16 = 2_000 +由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。
+要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。在下面的例子中,常量
+twoThousand是UInt16类型,然而常量one是UInt8类型。它们不能直接相加,因为它们类型不同。所以要调用UInt16(one)来创建一个新的UInt16数字并用one的值来初始化,然后使用这个新数字来计算:let twoThousand: UInt16 = 2_000 let one: UInt8 = 1 let twoThousandAndOne = twoThousand + UInt16(one) -现在两个数字的类型都是
-UInt16,可以进行相加。目标常量twoThousandAndOne的类型被推测为UInt16,因为它是两个UInt16值的合。+
SomeType(ofInitialValue)是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,UInt16有一个构造器,可以接受一个UInt8类型的值,所以这个构造器可以用现有的UInt8来创建一个新的UInt16。注意,你并不能传入任意类型的值,只能传入UInt16内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),详情参见扩展(链接待添加).现在两个数字的类型都是
+UInt16,可以进行相加。目标常量twoThousandAndOne的类型被推断为UInt16,因为它是两个UInt16值的和。
SomeType(ofInitialValue)是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,UInt16有一个构造器,可以接受一个UInt8类型的值,所以这个构造器可以用现有的UInt8来创建一个新的UInt16。注意,你并不能传入任意类型的值,只能传入UInt16内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考扩展。整数和浮点数转换
整数和浮点数的转换必须显式指定类型:
-+let three = 3 +let three = 3 let pointOneFourOneFiveNine = 0.14159 let pi = Double(three) + pointOneFourOneFiveNine // pi 等于 3.14159,所以被推测为 Double 类型 -这个例子中,常量
-three的值被用来创建一个Double类型的值,所以加号两边的数类型相同。如果不进行转换,两者无法相加。浮点数转换为整数也一样,整数类型可以用
-Double或者Float类型来初始化:+let integerPi = Int(pi) +这个例子中,常量
+three的值被用来创建一个Double类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。浮点数到整数的反向转换同样行,整数类型可以用
+Double或者Float类型来初始化:let integerPi = Int(pi) // integerPi 等于 3,所以被推测为 Int 类型 -当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说
+4.75会变成4,-3.9会变成-3。当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说
4.75会变成4,-3.9会变成-3。-+注意:结合数字类常量和变量不同于结合数字类原始值。原始值
+3可以直接和原始值0.14159相加,因为数字原始值本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。注意:
结合数字类常量和变量不同于结合数字类字面量。字面量3可以直接和字面量0.14159相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。类型别名
-类型别名就是给现有类型定义一个可选名字。你可以使用
+typealias关键字来定义类型别名。类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用
typealias关键字来定义类型别名。当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
-typealias AudioSample = UInt16 -定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
-+var maxAmplitudeFound = AudioSample.min ++typealias AudioSample = UInt16 +定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
+var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound 现在是 0 -本例中,
+AudioSample被定义为UInt16的一个别名。因为它是别名,AudioSample.min实际上是UInt16.min,所以会给maxAmplitudeFound赋一个初值0。本例中,
+AudioSample被定义为UInt16的一个别名。因为它是别名,AudioSample.min实际上是UInt16.min,所以会给maxAmplitudeFound赋一个初值0。布尔值
-Swift 有一个基本的布尔类型,叫做
-Bool。布尔值是指逻辑,因为它们只能是真或者假。Swift 有两个布尔常量,true和false:+let orangesAreOrange = true +Swift 有一个基本的布尔(Boolean)类型,叫做
+Bool。布尔值指逻辑上的(logical),因为它们只能是真或者假。Swift 有两个布尔常量,true和false:let orangesAreOrange = true let turnipsAreDelicious = false -+
orangesAreOrange和turnipsAreDelicious的类型会被推测为Bool,因为它们的初值是布尔原始值。就像之前提到的Int和Double一样,如果你创建变量的时候给它们赋值true或者false,那你不需要给常量或者变量标明Bool类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推测,这让 Swift 代码更加简洁并且可读性更高。
orangesAreOrange和turnipsAreDelicious的类型会被推断为Bool,因为它们的初值是布尔字面量。就像之前提到的Int和Double一样,如果你创建变量的时候给它们赋值true或者false,那你不需要将常量或者变量声明为Bool类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。当你编写条件语句比如
-if语句的时候,布尔值非常有用:+if turnipsAreDelicious { +if turnipsAreDelicious { println("Mmm, tasty turnips!") } else { println("Eww, turnips are horrible.") } // 输出 "Eww, turnips are horrible." -条件语句比如
+if语句的详细介绍参见控制流(待添加链接)。条件语句,例如
if,请参考控制流。如果你在需要使用
-Bool类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:+let i = 1 +let i = 1 if i { // 这个例子不会通过编译,会报错 } -然而,下面的例子是合法的:
-+let i = 1 +然而,下面的例子是合法的:
+let i = 1 if i == 1 { // 这个例子会编译成功 } --
i == 1的比较结果是Bool类型,所以第二个例子可以通过类型检查。类似i == 1这样的比较会在基本操作符(待添加链接)中详细讨论。和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的作用总是在意料之中。
++
i == 1的比较结果是Bool类型,所以第二个例子可以通过类型检查。类似i == 1这样的比较,请参考基本操作符。和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
+元组
-元组把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相同类型。
-下面这个例子中,
-(404, "Not Found")是一个描述 HTTP 状态码的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个404 Not Found状态码。+let http404Error = (404, "Not Found") +元组(tuples)把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相同类型。
+下面这个例子中,
+(404, "Not Found")是一个描述 HTTP 状态码(HTTP status code)的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个404 Not Found状态码。let http404Error = (404, "Not Found") // http404Error 的类型是 (Int, String),值是 (404, "Not Found") --
(404, "Not Found")元组把一个Int值和一个String值组合起来表示 HTTP 状态码的两个部分:一个数字和一个可以读懂的描述。这个元组可以被描述为“一个类型为(Int, String)的元组”。你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为
-(Int, Int, Int)或者(String, Bool)或者包含其他类型的元组。你可以将一个元组的内容分解成单独的常量和变量,然后你就可以正常使用它们了:
-+let (statusCode, statusMessage) = http404Error ++
(404, "Not Found")元组把一个Int值和一个String值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为(Int, String)的元组”。你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为
+(Int, Int, Int)或者(String, Bool)或者其他任何你想要的组合的元组。你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:
+let (statusCode, statusMessage) = http404Error println("The status code is \(statusCode)") // 输出 "The status code is 404" println("The status message is \(statusMessage)") // 输出 "The status message is Not Found" -如果你只需要一部分元组值,分解的时候可以把要忽略的部分设置成
-_:+let (justTheStatusCode, _) = http404Error +如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(
+_)标记:let (justTheStatusCode, _) = http404Error println("The status code is \(justTheStatusCode)") // 输出 "The status code is 404" -此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
-+println("The status code is \(http404Error.0)") +此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
+println("The status code is \(http404Error.0)") // 输出 "The status code is 404" println("The status message is \(http404Error.1)") // 输出 "The status message is Not Found" -你可以在定义元组的时候给单个元素命名:
-let http200Status = (statusCode: 200, description: "OK") -给元组中的元素命名后,你可以通过名字来获取这些元素的值:
-+println("The status code is \(http200Status.statusCode)") +你可以在定义元组的时候给单个元素命名:
++let http200Status = (statusCode: 200, description: "OK") +给元组中的元素命名后,你可以通过名字来获取这些元素的值:
+println("The status code is \(http200Status.statusCode)") // 输出 "The status code is 200" println("The status message is \(http200Status.description)") // 输出 "The status message is OK" -作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个
+(Int, String)元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。详情参见返回多个值的函数(待添加链接)。作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个
(Int, String)元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考函数参数与返回值。--注意:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。详情参见
+类和结构体(待添加链接)。注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考类和结构体。可选
-使用可选来处理值可能缺失的情况。可选表示:
+ +可选类型
+使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示:
-
- 有值,等于 x
+- 有值,等于 x
或者
-
- 没有值
+- 没有值
-注意:C 和 Objective-C 中并没有可选这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回
+nil,nil表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选可以让你暗示任意类型的值缺失,并不需要一个特殊值。注意:
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回nil,nil表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示任意类型的值缺失,并不需要一个特殊值。来看一个例子。Swift 的
String类型有一个叫做toInt的方法,作用是将一个String值转换成一个Int值。然而,并不是所有的字符串都可以转换成一个整数。字符串"123"可以被转换成数字123,但是字符串"hello, world"不行。下面的例子使用
-toInt方法来尝试将一个String转换成Int:+let possibleNumber = "123" +let possibleNumber = "123" let convertedNumber = possibleNumber.toInt() // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int" -因为
+toInt方法可能会失败,所以它返回一个可选的Int,而不是一个Int。一个可选的Int被写作Int?而不是Int。问号暗示包含的值是可选,也就是说可能包含Int值也可能不包含值。(不能包含其他任何值比如Bool值或者String值。只能是Int或者什么都没有。)因为
toInt方法可能会失败,所以它返回一个可选类型(optional)Int,而不是一个Int。一个可选的Int被写作Int?而不是Int。问号暗示包含的值是可选类型,也就是说可能包含Int值也可能不包含值。(不能包含其他任何值比如Bool值或者String值。只能是Int或者什么都没有。)if 语句以及强制解析
-你可以使用
-if语句来判断一个可选是否包含值。如果可选有值,结果是true;如果没有值,结果是false。当你确定可选包含值之后,你可以在可选的名字后面加一个
-!来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析:+if convertedNumber { +你可以使用
+if语句来判断一个可选是否包含值。如果可选类型有值,结果是true;如果没有值,结果是false。当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(
+!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):if convertedNumber { println("\(possibleNumber) has an integer value of \(convertedNumber!)") } else { println("\(possibleNumber) could not be converted to an integer") } // 输出 "123 has an integer value of 123" -更多关于
+if语句的内容参见控制流(待添加链接)。更多关于
if语句的内容,请参考控制流。-+注意:使用
+!来获取一个不存在的可选值会导致运行时错误。。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。注意:
使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。可选绑定
-使用可选绑定来判断可选是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在
-if和while语句中来对可选的值进行判断并把值赋给一个常量或者变量。if和while语句详情参见控制流。像下面这样写一个可选绑定:
-+if let constantName = someOptional { +使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在
+if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。if和while语句,请参考控制流。像下面这样在
+if语句中写一个可选绑定:if let constantName = someOptional { statements } -你可以像上面这样使用可选绑定来重写
-possibleNumber这个例子:+if let actualNumber = possibleNumber.toInt() { +你可以像上面这样使用可选绑定来重写
+possibleNumber这个例子:if let actualNumber = possibleNumber.toInt() { println("\(possibleNumber) has an integer value of \(actualNumber)") } else { println("\(possibleNumber) could not be converted to an integer") } // 输出 "123 has an integer value of 123" -这段代码可以被理解为:
+这段代码可以被理解为:
“如果
-possibleNumber.toInt返回的可选Int包含一个值,创建一个叫做actualNumber的新常量并将可选包含的值赋给它。”如果转换成功,
-actualNumber常量可以在if语句的第一个分支中使用。它已经被可选包含的值初始化过,所以不需要再使用!后缀来获取它的值。在这个例子中,actualNumber只被用来输出转换结果。你可以在可选绑定中使用常量和变量。如果你想在
+if语句的第一个分支中操作actualNumber的值,你可以改成if var actualNumber,这样可选包含的值就会被赋给一个变量。如果转换成功,
+actualNumber常量可以在if语句的第一个分支中使用。它已经被可选类型包含的值初始化过,所以不需要再使用!后缀来获取它的值。在这个例子中,actualNumber只被用来输出转换结果。你可以在可选绑定中使用常量和变量。如果你想在
if语句的第一个分支中操作actualNumber的值,你可以改成if var actualNumber,这样可选类型包含的值就会被赋给一个变量而非常量。nil
你可以给可选变量赋值为
-nil来表示它没有值:+var serverResponseCode: Int? = 404 +var serverResponseCode: Int? = 404 // serverResponseCode 包含一个可选的 Int 值 404 serverResponseCode = nil // serverResponseCode 现在不包含值 --注意:
+nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。+注意:
nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为
-nil:+var surveyAnswer: String? +var surveyAnswer: String? // surveyAnswer 被自动设置为 nil --注意:Swift 的
+nil和 Objective-C 中的nil并不一样。在 Objective-C 中,nil是一个指向不存在对象的指针。在 Swift 中,nil不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选都可以被设置为nil,不只是对象类型。+-注意:
Swift 的nil和 Objective-C 中的nil并不一样。在 Objective-C 中,nil是一个指向不存在对象的指针。在 Swift 中,nil不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为nil,不只是对象类型。隐式解析可选
-如上所述,可选暗示了常量或者变量可以“没有值”。可选可以通过
-if语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。有时候在程序架构中,第一次被赋值之后,可以确定一个可选总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
-这种类型的可选被定义为隐式解析可选。把后缀
-?改成!来声明一个隐式解析可选,比如String!。当可选被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选非常有用。隐式解析可选主要被用在 Swift 中类的构造过程中,详情参见
-无主引用和隐式解析可选属性(Unowned References and Implicitly Unwrapped Optional Properties待添加链接)。一个隐式解析可选其实就是一个普通的可选,但是可以被当做非可选来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选
-String和隐式解析可选String之间的区别:+let possibleString: String? = "An optional string." +隐式解析可选类型
+如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过
+if语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
+这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(
+String?)改成感叹号(String!)来声明一个隐式解析可选类型。当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考类实例之间的循环强引用。
+一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型
+String和隐式解析可选类型String之间的区别:+let possibleString: String? = "An optional string." println(possibleString!) // 需要惊叹号来获取值 // 输出 "An optional string." - -let assumedString: String! = "An implicitly unwrapped optional string." -println(assumedString) // 不需要惊叹号 +let assumedString: String! = "An implicitly unwrapped optional string." +println(assumedString) // 不需要感叹号 // 输出 "An implicitly unwrapped optional string." -你可以把隐式解析可选当做一个可以自动解析的可选。你要做的只是声明的时候把惊叹号放到类型的结尾,而不是每次获取值的变量结尾。
+你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
--注意:如果你在隐式解析可选没有值的时候尝试获取,会触发运行时错误。和你在没有值的普通可选后面加一个惊叹号一样。
+注意:
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。你仍然可以把隐式解析可选当做普通可选来判断它是否包含值: - if assumedString { - println(assumedString) - } - // 输出 "An implicitly unwrapped optional string."
-你也可以在可选绑定中使用隐式解析可选来检查并解析它的值: - if let definiteString = assumedString { - println(definiteString) - } - // 输出 "An implicitly unwrapped optional string."
+你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
++if assumedString { + println(assumedString) +} +// 输出 "An implicitly unwrapped optional string." +你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:
+if let definiteString = assumedString { + println(definiteString) +} +// 输出 "An implicitly unwrapped optional string." +-+注意:如果一个变量之后可能变成
+nil的话请不要使用隐式解析可选。如果你需要在变量的生命周期中判断是否是nil的话,请使用普通可选类型。注意:
如果一个变量之后可能变成nil的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil的话,请使用普通可选类型。断言
-可选可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行。这时,你可以在你的代码中触发一个断言来结束代码运行并通过调试来找到值缺失的原因。
-使用断言来调试
+可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行。这时,你可以在你的代码中触发一个断言(assertion)来结束代码运行并通过调试来找到值缺失的原因。
+使用断言进行调试
断言会在运行时判断一个逻辑条件是否为
true。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为true,代码运行会继续进行;如果条件判断为false,代码运行停止,你的应用被终止。如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
-你可以使用全局
-assert函数来写一个断言。给assert函数传入一个结果为true或者false的表达式以及一条信息,当表达式为false的时候这条信息会被显示:+let age = -3 +你可以使用全局
+assert函数来写一个断言。向assert函数传入一个结果为true或者false的表达式以及一条信息,当表达式为false的时候这条信息会被显示:let age = -3 assert(age >= 0, "A person's age cannot be less than zero") // 因为 age < 0,所以断言会触发 -在这个例子中,只有
+age >= 0为true的时候代码运行才会继续,也就是说,当age的值非负的时候。如果age的值是负数,就像代码中那样,age >= 0为false,断言被触发,结束应用。在这个例子中,只有
age >= 0为true的时候代码运行才会继续,也就是说,当age的值非负的时候。如果age的值是负数,就像代码中那样,age >= 0为false,断言被触发,结束应用。断言信息不能使用字符串插值。断言信息可以省略,就像这样:
-assert(age >= 0) -何时使用断言
-当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。断言的适用情景:
++assert(age >= 0) +何时使用断言
+当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。断言的适用情景:
-
-- 整数的下标(subscript)索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。
+- 整数类型的下标索引被传入一个自定义下标脚本实现,但是下标索引值可能太小或者太大。
- 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
- 一个可选值现在是
nil,但是后面的代码运行需要一个非nil值。查看
+下标(链接待添加)和函数(链接待添加)。-@@ -984,16 +1073,8 @@ assert(age >= 0, "A person's age cannot be less than zero")注意:断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
+注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
翻译无任何商业目的,仅供内部学习交流使用!
运算符是检查, 改变, 合并值的特殊符号或短语. 例如, 加号 + 把计算两个数的和(如 let i = 1 + 2). 复杂些的运行算包括逻辑与&&(如 if enteredDoorCode && passedRetinaScan), 还有自增运算符 ++i 这样让自身加一的便捷运算.
Swift支持大部分标准C语言的运算符, 且改进许多特性来减少常规编码错误. 如, 赋值符 = 不返回值, 以防止错把等号 == 写成赋值号 = 而导致Bug. 数值运算符( + , -, *, /, %等)会检测并不允许值溢出, 以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果. 当然允许你选择使用Swift的溢出运算符来玩溢出. 具体使用请移步溢出运算符.
区别于C语言, 在Swift中你可以对浮点数进行取余运算( % ), 还提供了C语言没有的表达两数之间的值的区间运算符, ( a..b 和 a...b ), 这方便我们表达一个区间内的数值.
本章节只描述了Swift中的简单运算符, 高级运算符包含了高级运算符,及如何自定义运算符, 及如何进行自定义类型的运算符重载.
-运算符有一目, 双目和三目运算符.
-一目运算符对单一操作对象操作, 如 -a.
一目运算符分前置符和后置运算符, 前置运算符需紧排操作对象之前, 如 !b, 后置运算符需紧跟操作对象之后,如 i++,
双目运算符操作两个操作对象, 如 2 + 3. 是中置的, 因为它们出现在两个操作对象之间.
三目运算符操作三个操作对象, 和C语言一样, Swift只有一个三目运算符, 就是三目条件运算符 a ? b : c.
受运算符影响的值叫操作数, 在表达式 1 + 2 中, 加号 + 是双目运算符, 它的两个操作数是值 1 和 2.
赋值运算 a = b, 表示用 b 的值来初始化或更新 a 的值.
++翻译:xielingwang
+
校对:Evilcome
本页包含内容:
+运算符是检查、改变、合并值的特殊符号或短语。例如,加号+将两个数相加(如let i = 1 + 2)。复杂些的运算例如逻辑与运算符&&(如if enteredDoorCode && passedRetinaScan),或让 i 值加1的便捷自增运算符++i等。
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(=)不返回值,以防止把想要判断相等运算符(==)的地方写成赋值符导致的错误。数值运算符(+,-,*,/,%等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见溢出运算符。
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(%),Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符,(a..b和a...b),这方便我们表达一个区间内的数值。
本章节只描述了 Swift 中的基本运算符,高级运算符包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
+ +运算符有一元、二元和三元运算符。
+-a)。一元运算符分前置符和后置运算符,前置运算符需紧排操作对象之前(如!b),后置运算符需紧跟操作对象之后(如i++)。2 + 3),是中置的,因为它们出现在两个操作对象之间。a ? b : c)。受运算符影响的值叫操作数,在表达式1 + 2中,加号+是二元运算符,它的两个操作数是值1和2。
赋值运算(a = b),表示用b的值来初始化或更新a的值:
let b = 10
var a = 5
a = b
// a 现在等于 10
-如果赋值的右边是一个多元组, 它的元素可以马上被分解多个变量或变量
-let (x, y) = (1, 2)
+如果赋值的右边是一个多元组,它的元素可以马上被分解多个变量或变量:
+let (x, y) = (1, 2)
// 现在 x 等于 1, y 等于 2
-
与C语言和Objective-C不同, Swift的赋值操作并不返回任何值. 所以以下代码是错误的:
+
+与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
if x = y {
// 此句错误, 因为 x = y 并不返回任何值
}
-这个特性使得你不无法把==错写成=了, 由于if x = y是错误代码, Swift从底层帮你避免了这些代码错误.
Swift让所有数值类型都支持了基本的四则运算:
+这个特性使你无法把(==)错写成(=),由于if x = y是错误代码,Swift 从底层帮你避免了这些错误代码。
Swift 中所有数值类型都支持了基本的四则运算:
+-*/+)-)*)/)1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
-与C语言和Objective-C不同的是, Swift默认不允许在数值运算中出现溢出情况. 但你可以使用Swift的溢出运算符来达到你有目的的溢出, (如 a &+ b ). 详情请移步: 溢出运算符.
加法操作 + 也用于字符串的拼接:
与 C 语言和 Objective-C 不同的是,Swift 默认不允许在数值运算中出现溢出情况。但你可以使用 Swift 的溢出运算符来达到你有目的的溢出(如a &+ b)。详情参见溢出运算符。
加法运算符也可用于String的拼接:
"hello, " + "world" // 等于 "hello, world"
-两个字符类型或一个字符类型和一个字符串类型, 相加会生成一个新的字符串类型:
+两个Character值或一个String和一个Character值,相加会生成一个新的String值:
let dog: Character = "d"
let cow: Character = "c"
let dogCow = dog + cow
-// 译者注: 原谅的引号内是很可爱的小狗和小牛, 但win os下不支持表情字符, 所以改成了普通字符
+// 译者注: 原来的引号内是很可爱的小狗和小牛, 但win os下不支持表情字符, 所以改成了普通字符
// dogCow 现在是 "dc"
-详细请点击 字符,字符串的拼接.
-求余运算 a % b 是计算 b 的多少倍刚刚好可以容入 a , 多出来的那部分叫余数.
详情参见字符,字符串的拼接。
+求余运算(a % b)是计算b的多少倍刚刚好可以容入a,返回多出来的那部分(余数)。
--注意
-求余运算(%)在其他语言也叫取模运算. 然而严格说来, 我们看该运算符对负数的操作结果,
+求余比取模更合适些.注意:
求余运算(%)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。
我们来谈谈取余是怎么回事, 计算 9 % 4, 你先计算出4的多少倍会刚好可以容入 9 中.
2倍, 非常好, 那余数是1 (用'*'标出)
-| 1 | -2 | -3 | -4 | -5 | -6 | -7 | -8 | -9 | -
| 4 | -4 | -1* | -||||||
在Swift中这么来表达
+我们来谈谈取余是怎么回事,计算9 % 4,你先计算出4的多少倍会刚好可以容入9中:

2倍,非常好,那余数是1(用橙色标出)
+在 Swift 中这么来表达:
9 % 4 // 等于 1
-为了得到 a % b 的结果, %计算了以下等式, 并输出余数作为结果:
a = (b × 倍数) + 余数
-当倍数取最大值的时候, 就会刚好可以容入 a 中.
把 9 和 4 代入等式中, 我们得 1:
为了得到a % b的结果,%计算了以下等式,并输出余数作为结果:
a = (b × 倍数) + 余数
+当倍数取最大值的时候,就会刚好可以容入a中。
把9和4代入等式中,我们得1:
9 = (4 × 2) + 1
-同样的方法, 我来们计算 -9 % 4 :
-9 % 4 // 等于 -1
-把 -9 和 4 代入等式, -2 是取到的最大整数:
同样的方法,我来们计算 -9 % 4:
-9 % 4 // 等于 -1
+
+把-9和4代入等式,-2是取到的最大整数:
-9 = (4 × -2) + -1
-余数是 -1.
在对负数 -b 求余时, -b的符号会被忽略. 这意味着 a % b 和 a % -b的结果是相同的.
不同于C和Objective-C, Swift中是可以对浮点数进行求余的.
+余数是-1。
在对负数b求余时,b的符号会被忽略。这意味着 a % b 和 a % -b的结果是相同的。
不同于 C 语言和 Objective-C,Swift 中是可以对浮点数进行求余的。
8 % 2.5 // 等于 0.5
-这个例子中, 8除于2.5等于3余0.5, 所以结果是0.5.
-和C一样, Swift也提供了方便对变量本身加1或减1的自增 ++ 和自减 -- 的运算符. 其操作对象可以是整形和浮点型。
+
这个例子中,8除于2.5等于3余0.5,所以结果是一个Double值0.5。

和 C 语言一样,Swift 也提供了方便对变量本身加1或减1的自增(++)和自减(--)的运算符。其操作对象可以是整形和浮点型。
var i = 0
+var i = 0
++i // 现在 i = 1
-
每调用一次 ++i, i 的值就会加1.
-实际上, ++i 是 i = i + 1 的简写, 而 --i 是 i = i - 1的简写.
-++ 和 --既是前置又是后置运算. ++i, i++, --i 和 i-- 都是有效的写法.
-我们需要注意的是这些运算符修改了 i 后有一个返回值. 如果你只想修改 i 的值, 那你就可以忽略这个返回值. 但如果你想使用返回值, 你就需要留意前置和后置操作的返回值是不同的.
-当 ++ 前置的时候, 先自増再返回.
-当 ++ 后置的时候, 先返回再自增.
-不懂? 我们看例子:
+
+每调用一次++i,i的值就会加1。实际上,++i是i = i + 1的简写,而--i是i = i - 1的简写。
++和--既是前置又是后置运算。++i,i++,--i和i--都是有效的写法。
我们需要注意的是这些运算符修改了i后有一个返回值。如果你只想修改i的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的。
当++前置的时候,先自増再返回。
当++后置的时候,先返回再自增。
例如:
var a = 0
let b = ++a // a 和 b 现在都是 1
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
-上述例子, let b = ++a, 先把 a 加1了再返回 a 的值. 所以 a 和 b 都是新值 1.
而 let c = a++, 是先返回了 a 的值, 然后 a 才加1. 所以 c 得到了 a 的旧值1, 而 a 加1后变成2.
除非你需要使用 i++ 的特性, 不然推荐你使用 ++i 和 --i, 因为先修改后返回这样的行为更符合我们的逻辑.
数值的正负号可以使用前缀 - (即单目负号) 来切换:
上述例子,let b = ++a先把a加1了再返回a的值。所以a和b都是新值1。
而let c = a++,是先返回了a的值,然后a才加1。所以c得到了a的旧值1,而a加1后变成2。
除非你需要使用i++的特性,不然推荐你使用++i和--i,因为先修改后返回这样的行为更符合我们的逻辑。
数值的正负号可以使用前缀-(即一元负号)来切换:
let three = 3
let minusThree = -three // minusThree 等于 -3
-let plusThree = -minusThree // plusThree 等于 3, o或 "负负3"
+let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
-单目负号写在操作数之前, 中间没有空格.
-单目正号 + 不做任何改变地返回操作数的值.
一元负号(-)写在操作数之前,中间没有空格。
一元正号(+)不做任何改变地返回操作数的值。
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
-虽然单目 + 做无用功, 但当你在使用单目负号来表达负数时, 你可以使用单目正号来表达正数, 如此你的代码会具有对称美.
如同强大的C语言, Swift也提供把其他运算符和赋值运算 = 组合的复合赋值运算符, 加赋运算 += 是其中一个例子:
虽然一元+做无用功,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
如同强大的 C 语言,Swift 也提供把其他运算符和赋值运算(=)组合的复合赋值运算符,加赋运算(+=)是其中一个例子:
var a = 1
a += 2 // a 现在是 3
-表达式 a += 2 是 a = a + 2 的简写, 一个加赋运算就把加法和赋值两件事完成了.
表达式a += 2是a = a + 2的简写,一个加赋运算就把加法和赋值两件事完成了。
--注意:
-复合赋值运算没有返回值,
+let b = a += 2这类代码是错误. 这不同于上面提到的自增和自减运算符.注意:
复合赋值运算没有返回值,let b = a += 2这类代码是错误。这不同于上面提到的自增和自减运算符。
表达式里有复合运算符的完整列表. -
-所有标准C中的比较运算都可以在Swift中使用.
+在表达式章节里有复合运算符的完整列表。 + +
+所有标准 C 语言中的比较运算都可以在 Swift 中使用。
a == ba != ba > ba < ba >= ba <= ba == b)a != b)a > b)a < b)a >= b)a <= b)--注意:
-Swift也提供恒等
+===和不恒等!==这两个比较符来判断两个对象是否引用同一个对象实例. 更多细节在 类与结构.注意:
Swift 也提供恒等===和不恒等!==这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在类与结构。
每个比较运算都返回了一个标识表达式是否成立的布尔值:
+每个比较运算都返回了一个标识表达式是否成立的布尔值:
1 == 1 // true, 因为 1 等于 1
2 != 1 // true, 因为 2 不等于 1
2 > 1 // true, 因为 2 大于 1
@@ -756,33 +771,33 @@ a += 2 // a 现在是 3
1 >= 1 // true, 因为 1 大于等于 1
2 <= 1 // false, 因为 2 并不小于等于 1
-比较运算多用于条件语句, 如 if 条件:
比较运算多用于条件语句,如if条件:
let name = "world"
if name == "world" {
println("hello, world")
} else {
- println("对不起, \(name), 我不认识你!")
+ println("I'm sorry \(name), but I don't recognize you")
}
// 输出 "hello, world", 因为 `name` 就是等于 "world"
-关于 if 语句, 请看 控制流.
三目条件运算的特殊在于它是有三个操作数的运算符, 它的原型是 问题 ? 答案1 : 答案2. 它简洁地表达根据 问题 成立与否作出二选一的操作. 如果 问题 成立, 返回 答案1 的结果; 如果不成立, 返回 答案2 的结果.
使用三目条件运算简化了以下代码:
+关于if语句,请看控制流。
三元条件运算的特殊在于它是有三个操作数的运算符,它的原型是 问题 ? 答案1 : 答案2。它简洁地表达根据问题成立与否作出二选一的操作。如果问题成立,返回答案1的结果; 如果不成立,返回答案2的结果。
使用三元条件运算简化了以下代码:
if question: {
answer1
-}
-else {
+} else {
answer2
}
-这里有个计算表格行高的例子. 如果有表头, 那行高应比内容高度要高出50像素; 如果没有表头, 只需高出20像素.
+这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 现在是 90
-这样写会比下面的代码简洁:
+这样写会比下面的代码简洁:
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
@@ -793,16 +808,17 @@ if hasHeader {
}
// rowHeight 现在是 90
-第一段代码例子使用了三目条件运算, 所以一行代码就能让我们得到正确答案. 这比第二段代码简洁得多, 无需将 rowHeight 定义成变量, 因为它的值无需在 if 语句中改变.
三目条件运算提供有效率且便捷的方式来表达二选一的选择. 需要注意的事, 过度使用三目条件运算就会由简洁的代码变成难懂的代码. 我们应避免在一个组合语句使用多个三目条件运算符.
-Swift提供了两个方便表达一个区间的值的运算符.
-闭区间运算符 a...b 定义一个包含从 a 到 b (包括 a 和 b)的所有值的区间.
+
第一段代码例子使用了三元条件运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将rowHeight定义成变量,因为它的值无需在if语句中改变。
三元条件运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三元条件运算就会由简洁的代码变成难懂的代码。我们应避免在一个组合语句使用多个三元条件运算符。
+ +Swift 提供了两个方便表达一个区间的值的运算符。
+闭区间运算符(a...b)定义一个包含从a到b(包括a和b)的所有值的区间。
-闭区间运算符在迭代一个区间的所有值时是非常有用的, 如在 for-in 循环中:
for-in循环中:
for index in 1...5 {
- println("\(index) * 5 = \(index * 5)")
+ println("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
@@ -810,11 +826,11 @@ if hasHeader {
// 4 * 5 = 20
// 5 * 5 = 25
-关于 for-in, 请看 控制流.
半闭区间 a..b 定义一个从 a 到 b 但不包括 b 的区间.
-之所以称为半闭区间, 是因为该区间包含第一个值而不包括最后的值.
半闭区间的实用性在于当你使用一个0始的列表(如数组)时, 非常方便地从0数到列表的长度.
+关于for-in,请看控制流。
半闭区间(a..b)定义一个从a到b但不包括b的区间。
+之所以称为半闭区间,是因为该区间包含第一个值而不包括最后的值。
半闭区间的实用性在于当你使用一个0始的列表(如数组)时,非常方便地从0数到列表的长度。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..count {
@@ -825,31 +841,30 @@ for i in 0..count {
// 第 3 个人叫 Brian
// 第 4 个人叫 Jack
---注意: 数组有4个元素, 但
-0..count只数到 3 (最后一个元素的下标), 因为它是半闭区间. 关于数组, 请查阅 数组.
逻辑运算的操作对象是逻辑布尔值. Swift支持基于C语言的三个标准逻辑运算.
+数组有4个元素,但0..count只数到3(最后一个元素的下标),因为它是半闭区间。关于数组,请查阅数组。
逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
!aa && ba || b!a)a && b)a || b)逻辑非运算 !a 对一个布尔值取反, 使得 true 变 false, false 变 true.
它是一个前置运算符, 需出现在操作数之前, 且不加空格. 读作 非 a, 然后我们看以下例子:
逻辑非运算(!a)对一个布尔值取反,使得true变false,false变true。
它是一个前置运算符,需出现在操作数之前,且不加空格。读作非 a,然后我们看以下例子:
let allowedEntry = false
if !allowedEntry {
println("ACCESS DENIED")
}
-// prints "ACCESS DENIED"
+// 输出 "ACCESS DENIED"
-if !allowedEntry语句可以读作 "如果 非 alowed entry.", 接下一行代码只有在如果 "非 allow entry" 为 true, 即 allowEntry 为 false 时被执行.
在示例代码中, 小心地选择布尔常量或变量有助于代码的可读性, 并且避免使用双重逻辑非运算, 或混乱的逻辑语句.
-逻辑与 a && b 表达了只有 a 和 b 的值都为 true 时, 整个表达式的值才会是 true .
只要任意一个值为 false, 整个表达式的值就为 false. 事实上, 如果第一个值为 false, 那么是不去计算第二个值的, 因为它已经不可能影响整个表达式的结果了. 这被称做 "短路计算".
以下例子, 只有两个值都为值的时候才允许进入:
+if !allowedEntry语句可以读作 "如果 非 alowed entry。",接下一行代码只有在如果 "非 allow entry" 为true,即allowEntry为false时被执行。
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
+逻辑与(a && b)表达了只有a和b的值都为true时,整个表达式的值才会是true。
只要任意一个值为false,整个表达式的值就为false。事实上,如果第一个值为false,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做 "短路计算(short-circuit evaluation)"。
以下例子,只有两个Bool值都为true值的时候才允许进入:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
@@ -857,12 +872,12 @@ if enteredDoorCode && passedRetinaScan {
} else {
println("ACCESS DENIED")
}
-// 输出 "ACCESS DENIED
+// 输出 "ACCESS DENIED"
-逻辑或 a || b 是一个由两个连续的 | 组成的中置运算符. 它表示了两个逻辑表达式的其中一个为 true, 整个表达式就为 true.
同逻辑与运算类似, 逻辑或也是"短路计算"的, 当左端的表达式为 true 时, 将不计算右边的表达式了, 因为它不可能改变整个表达式的值了.
以下示例代码中, 第一个布尔值 hasDoorKey 为 false, 但第二个值 knowsOverridePassword 为 true, 所以整个表达是 true, 于是允许进入:
逻辑或(a || b)是一个由两个连续的|组成的中置运算符。它表示了两个逻辑表达式的其中一个为true,整个表达式就为true。
同逻辑与运算类似,逻辑或也是"短路计算"的,当左端的表达式为true时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
以下示例代码中,第一个布尔值(hasDoorKey)为false,但第二个值(knowsOverridePassword)为true,所以整个表达是true,于是允许进入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
@@ -872,8 +887,8 @@ if hasDoorKey || knowsOverridePassword {
}
// 输出 "Welcome!"
-我们可以组合多个逻辑运算来表达一个复合逻辑:
+我们可以组合多个逻辑运算来表达一个复合逻辑:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
println("Welcome!")
} else {
@@ -881,20 +896,19 @@ if hasDoorKey || knowsOverridePassword {
}
// 输出 "Welcome!"
-这个例子使用了含多个 && 和 || 的复合逻辑. 但无论怎样, && 和 || 始终只能操作两个值. 所以这实际是三个简单逻辑连续操作的结果. 我们来解读一下:
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码, 我们就能把门打开进入.
-前两种情况, 我们都不满足, 所以前两个简单逻辑的结果是 false, 但是我们是知道紧急情况下重置的密码的, 所以整个复杂表达式的值还是 true.
为了一个复杂表达式更容易读懂, 在合适的地方使用括号来明确优先级是很有效的, 虽然它并非必要的. 在上个关于门的权限的例子中, 我们给第一个部分加个括号, 使用它看起来逻辑更明确.
+这个例子使用了含多个&&和||的复合逻辑。但无论怎样,&&和||始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
+前两种情况,我们都不满足,所以前两个简单逻辑的结果是false,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是true。
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
println("Welcome!")
} else {
println("ACCESS DENIED")
}
-// prints "Welcome!"
+// 输出 "Welcome!"
-这括号使得前两个值被看成整个逻辑表达中独立的一个部分. 虽然有括号和没括号的输出结果是一样的, 但对于读代码的人来说有括号的代码更清晰.
-可读性比简洁性更重要, 请在可以让你代码变清晰地地方加个括号吧!
+这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰地地方加个括号吧!
翻译无任何商业目的,仅供内部学习交流使用!
+ ++
本页包含内容:
String 是例如 "hello, world", "海贼王" 这样的有序的 Character (字符) 类型的值的集合,通过 String 类型来表示。
-Swift 的 String 和 Character 类型提供了一个快速的,兼容 Unicode 的方式来处理代码中的文本信息。 +
String是例如“hello, world”,“海贼王” 这样的有序的Character(字符)类型的值的集合,通过String类型来表示。
Swift 的String和Character类型提供了一个快速的,兼容 Unicode 的方式来处理代码中的文本信息。
创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
-字符串连接操作只需要简单地通过 + 号将两个字符串相连即可。
+字符串连接操作只需要简单地通过+号将两个字符串相连即可。
与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。
尽管语法简易,但 String 类型是一种快速、现代化的字符串实现。 -每一个字符串都是由独立编码的 Unicode 字符组成,并提供了以不同 Unicode 表示 (representations) 来访问这些字符的支持。
-Swift可以在常量、变量、字面量和表达式中进行字符串插值操作,可以轻松创建用于展示、存储和打印的自定义字符串。
+尽管语法简易,但String类型是一种快速、现代化的字符串实现。
+每一个字符串都是由独立编码的 Unicode 字符组成,并提供了以不同 Unicode 表示(representations)来访问这些字符的支持。
Swift 可以在常量、变量、字面量和表达式中进行字符串插值操作,可以轻松创建用于展示、存储和打印的自定义字符串。
--注意:
-Swift 的 String 类型与 Foundation NSString 类进行了无缝桥接。 -如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。 -所有 NSString API 都可以调用您创建的任意 String 类型的值。 -除此之外,还可以使用本章介绍的 String 特性。 -您也可以在任意要求传入 NSString 实例作为参数的 API 中使用 String 类型的值作为替代。
-更多关于在 Foundation 和 Cocoa 中使用 String 的信息请查看 Using Swift with Cocoa and Objective-C。
+注意:
Swift 的String类型与 FoundationNSString类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有NSStringAPI 都可以调用您创建的任意String类型的值。除此之外,还可以使用本章介绍的String特性。您也可以在任意要求传入NSString实例作为参数的 API 中使用String类型的值作为替代。 +更多关于在 Foundation 和 Cocoa 中使用String的信息请查看 Using Swift with Cocoa and Objective-C。
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。 字符串字面量是由双引号 ("") 包裹着的具有固定顺序的文本字符集。
字符串字面量可以用于为常量和变量提供初始值。
-let someString = "Some string literal value"
--注意:
-+
someString变量通过字符串字面量进行初始化,Swift 因此推断该变量为 String 类型。+let someString = "Some string literal value" ++注意:
someString变量通过字符串字面量进行初始化,Swift 因此推断该变量为String类型。字符串字面量可以包含以下特殊字符:
-
- 转义字符
-\0(空字符)、\\(反斜线)、\t(水平制表符)、\n(换行符)、\r(回车符)、\"(双引号)、\'(单引号)。- 单字节 Unicode 标量,写成
-\xnn,其中nn为两位十六进制数。- 双字节 Unicode 标量,写成
-\unnnn,其中nnnn为四位十六进制数。- 四字节 Unicode 标量,写成
+\Unnnnnnnn,其中nnnnnnnn为八位十六进制数。- 转义字符
+\0(空字符)、\\(反斜线)、\t(水平制表符)、\n(换行符)、\r(回车符)、\"(双引号)、\'(单引号)。- 单字节 Unicode 标量,写成
+\xnn,其中nn为两位十六进制数。- 双字节 Unicode 标量,写成
+\unnnn,其中nnnn为四位十六进制数。- 四字节 Unicode 标量,写成
\Unnnnnnnn,其中nnnnnnnn为八位十六进制数。下面的代码为各种特殊字符的使用示例。 -
-wiseWords常量包含了两个转移特殊字符 (双括号); -dollarSign、blackHeart和sparklingHeart常量演示了三种不同格式的 Unicode 标量:+ +let wiseWords = "\"我是要成为海贼王的男人\" - 路飞" +wiseWords常量包含了两个转移特殊字符 (双括号); +dollarSign、blackHeart和sparklingHeart常量演示了三种不同格式的 Unicode 标量: +let wiseWords = "\"我是要成为海贼王的男人\" - 路飞" // "我是要成为海贼王的男人" - 路飞 -let dollarSign = "\x24" // $, Unicode 标量 U+0024 -let blackHeart = "\u2665" // ♥, Unicode 标量 U+2665 -let sparklingHeart = "\U0001F496" // 💖, Unicode 标量 U+1F496 -
-初始化空字符串
+let dollarSign = "\x24" // $, Unicode 标量 U+0024 +let blackHeart = "\u2665" // ♥, Unicode 标量 U+2665 +let sparklingHeart = "\U0001F496" // 💖, Unicode 标量 U+1F496 +初始化空字符串 (Initializing an Empty String)
为了构造一个很长的字符串,可以创建一个空字符串作为初始值。 -可以将空的字符串字面量赋值给变量,也可以初始化一个新的 String 实例:
-+ +var emptyString = "" // 空字符串字面量 +可以将空的字符串字面量赋值给变量,也可以初始化一个新的String实例: +var emptyString = "" // 空字符串字面量 var anotherEmptyString = String() // 初始化 String 实例 // 两个字符串均为空并等价。 -您可以通过检查其 Boolean 类型的
-isEmpty属性来判断该字符串是否为空:+if emptyString.isEmpty { +您可以通过检查其
+Boolean类型的isEmpty属性来判断该字符串是否为空:if emptyString.isEmpty { println("什么都没有") } -// 输出 "什么都没有" -
-字符串可变性
+// 打印输出:"什么都没有" +字符串可变性 (String Mutability)
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:
-+var variableString = "Horse" +var variableString = "Horse" variableString += " and carriage" // variableString 现在为 "Horse and carriage" let constantString = "Highlander" constantString += " and another Highlander" // 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。 --注意:
-在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(
+NSString和NSMutableString)来指定该字符串是否可以被修改,Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。+-注意:
在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(NSString和NSMutableString)来指定该字符串是否可以被修改,Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。
-字符串是值类型
-Swift 的 String 类型是值类型。 +
+字符串是值类型(Strings Are Value Types)
+Swift 的
+值类型在 结构体和枚举是值类型 中进行了说明。String类型是值类型。 如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作或在函数/方法中传递时,会进行值拷贝。 任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。 -值类型在 Structures and Enumerations Are Value Types 中进行了说明。-注意:
-与 Cocoa 中的 NSString 不同,当您在 Cocoa 中创建了一个 NSString 实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该 NSString 实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
+注意:
与 Cocoa 中的NSString不同,当您在 Cocoa 中创建了一个NSString实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该NSString实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。 很明显无论该值来自于哪里,都是您独自拥有的。 您可以放心您传递的字符串本身不会被更改。
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
-
-使用字符(Characters)
-Swift 的 String 类型表示特定序列的 Character (字符) 类型值的集合。 +
+使用字符(Working with Characters)
+Swift 的
-String类型表示特定序列的Character(字符) 类型值的集合。 每一个字符值代表一个 Unicode 字符。 -您可利用 for-in 循环来遍历字符串中的每一个字符:+for character in "Dog!🐶" { +您可利用for-in循环来遍历字符串中的每一个字符: +for character in "Dog!🐶" { println(character) } // D @@ -699,29 +707,26 @@ constantString += " and another Highlander" // g // ! // 🐶 -for-in 循环在For Loops中进行了详细描述。
-另外,通过标明一个 Character 类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量:
-let yenSign: Character = "¥" -
-计算字符数量
-通过调用全局
-countElements函数,并将字符串作为参数进行传递,可以获取该字符串的字符数量。+let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" +for-in 循环在 For Loops 中进行了详细描述。
+另外,通过标明一个
+Character类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量:+ +let yenSign: Character = "¥" +计算字符数量 (Counting Characters)
+通过调用全局
+countElements函数,并将字符串作为参数进行传递,可以获取该字符串的字符数量。let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" println("unusualMenagerie has \(countElements(unusualMenagerie)) characters") -// prints "unusualMenagerie has 40 characters" --注意:
-不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。 -所以Swift 中的字符在一个字符串中并不一定占用相同的内存空间。 -因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。 -如果您正在处理一个长字符串,需要注意
-countElements函数必须遍历字符串中的字符以精准计算字符串的长度。另外需要注意的是通过
+// 打印输出:"unusualMenagerie has 40 characters" +countElements返回的字符数量并不总是与包含相同字符的 NSString 的length属性相同。 -NSString 的length属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。 -为了解决这个问题,NSString 的length属性在被 Swift的 String 访问时会成为utf16count。+-注意:
不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意countElements函数必须遍历字符串中的字符以精准计算字符串的长度。 +另外需要注意的是通过countElements返回的字符数量并不总是与包含相同字符的NSString的length属性相同。NSString的length属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,NSString的length属性在被 Swift 的String访问时会成为utf16count。
-连接字符串和字符
-字符串和字符的值可以通过加法运算符 (
-+) 相加在一起并创建一个新的字符串值:+let string1 = "hello" + +连接字符串和字符 (Concatenating Strings and Characters)
+字符串和字符的值可以通过加法运算符(
++)相加在一起并创建一个新的字符串值:let string1 = "hello" let string2 = " there" let character1: Character = "!" let character2: Character = "?" @@ -730,51 +735,55 @@ let stringPlusCharacter = string1 + character1 // 等于 "hello!&quo let stringPlusString = string1 + string2 // 等于 "hello there" let characterPlusString = character1 + string1 // 等于 "!hello" let characterPlusCharacter = character1 + character2 // 等于 "!?" -您也可以通过加法赋值运算符 (+=) 将一个字符串或者字符添加到一个已经存在字符串变量上:
-+var instruction = "look over" +您也可以通过加法赋值运算符 (
++=) 将一个字符串或者字符添加到一个已经存在字符串变量上:var instruction = "look over" instruction += string2 // instruction 现在等于 "look over there" var welcome = "good morning" welcome += character1 // welcome 现在等于 "good morning!" --注意:
-您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
++-注意:
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
-字符串插值
+ +字符串插值 (String Interpolation)
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。 您插入的字符串字面量的每一项都被包裹在以反斜线为前缀的圆括号中:
-+let multiplier = 3 +let multiplier = 3 let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)" -// message is "3 乘以 2.5 是 7.5" -在上面的例子中,
-multiplier作为\(multiplier)被插入到一个字符串字面量中。 -当创建字符串执行插值计算时此占位符会被替换为multiplier实际的值。+// message 是 "3 乘以 2.5 是 7.5" +
multiplier的值也作为字符串中后面表达式的一部分。 -该表达式计算Double(multiplier) * 2.5的值并将结果 (7.5) 插入到字符串中。 -在这个例子中,表达式写为\(Double(multiplier) * 2.5)并包含在字符串字面量中。在上面的例子中,
+multiplier作为\(multiplier)被插入到一个字符串字面量中。 +当创建字符串执行插值计算时此占位符会被替换为multiplier实际的值。
multiplier的值也作为字符串中后面表达式的一部分。 +该表达式计算Double(multiplier) * 2.5的值并将结果 (7.5) 插入到字符串中。 +在这个例子中,表达式写为\(Double(multiplier) * 2.5)并包含在字符串字面量中。--注意:
-您插值字符串中写在括号中的表达式不能包含非转义双引号 (
+") 和反斜杠 (\),并且不能包含回车或换行符。注意:
插值字符串中写在括号中的表达式不能包含非转义双引号 (") 和反斜杠 (\),并且不能包含回车或换行符。
-比较字符串
-Swift 提供了三种方式来比较字符串的值:字符串相等,前缀相等和后缀相等。
-字符串相等
+ +比较字符串 (Comparing Strings)
+Swift 提供了三种方式来比较字符串的值:字符串相等、前缀相等和后缀相等。
+ +字符串相等 (String Equality)
如果两个字符串以同一顺序包含完全相同的字符,则认为两者字符串相等:
-+ +let quotation = "我们是一样一样滴." +let quotation = "我们是一样一样滴." let sameQuotation = "我们是一样一样滴." if quotation == sameQuotation { println("这两个字符串被认为是相同的") } -// prints "这两个字符串被认为是相同的" -前缀/后缀相等
-通过调用字符串的
hasPrefix/hasSuffix方法来检查字符串是否拥有特定前缀/后缀。 -两个方法均需要以字符串作为参数传入并传出 Boolean 值。 +// 打印输出:"这两个字符串被认为是相同的" +前缀/后缀相等 (Prefix and Suffix Equality)
+通过调用字符串的
-hasPrefix/hasSuffix方法来检查字符串是否拥有特定前缀/后缀。 +两个方法均需要以字符串作为参数传入并传出Boolean值。 两个方法均执行基本字符串和前缀/后缀字符串之间逐个字符的比较操作。下面的例子以一个字符串数组表示莎士比亚话剧
-罗密欧与朱丽叶中前两场的场景位置:+ +let romeoAndJuliet = [ +下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
+let romeoAndJuliet = [ "Act 1 Scene 1: Verona, A public place", "Act 1 Scene 2: Capulet's mansion", "Act 1 Scene 3: A room in Capulet's mansion", @@ -787,79 +796,105 @@ if quotation == sameQuotation { "Act 2 Scene 5: Capulet's mansion", "Act 2 Scene 6: Friar Lawrence's cell" ] -您可以利用
-hasPrefix方法来计算话剧中第一幕的场景数:+var act1SceneCount = 0 +您可以利用
+hasPrefix方法来计算话剧中第一幕的场景数:var act1SceneCount = 0 for scene in romeoAndJuliet { if scene.hasPrefix("Act 1 ") { ++act1SceneCount } } println("There are \(act1SceneCount) scenes in Act 1") -// prints "There are 5 scenes in Act 1" -大写和小写字符串
-您可以通过字符串的
-uppercaseString和lowercaseString属性来访问大写/小写版本的字符串。+let normal = "Could you help me, please?" +// 打印输出:"There are 5 scenes in Act 1" +相似地,您可以用
+hasSuffix方法来计算发生在不同地方的场景数:+ +var mansionCount = 0 +var cellCount = 0 +for scene in romeoAndJuliet { + if scene.hasSuffix("Capulet's mansion") { + ++mansionCount + } else if scene.hasSuffix("Friar Lawrence's cell") { + ++cellCount + } +} +println("\(mansionCount) mansion scenes; \(cellCount) cell scenes") +// 打印输出:"6 mansion scenes; 2 cell scenes” +大写和小写字符串(Uppercase and Lowercase Strings)
+您可以通过字符串的
+uppercaseString和lowercaseString属性来访问大写/小写版本的字符串。let normal = "Could you help me, please?" let shouty = normal.uppercaseString // shouty 值为 "COULD YOU HELP ME, PLEASE?" let whispered = normal.lowercaseString // whispered 值为 "could you help me, please?" -
-Unicode
+Unicode
Unicode 是一个国际标准,用于文本的编码和表示。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
Swift 的字符串和字符类型是完全兼容 Unicode 标准的,它支持如下所述的一系列不同的 Unicode 编码。
-Unicode 术语(Terminology)
+ +Unicode 术语(Unicode Terminology)
Unicode 中每一个字符都可以被解释为一个或多个 unicode 标量。 -字符的 unicode 标量是一个唯一的21位数字(和名称),例如
-U+0061表示小写的拉丁字母A ("a"),U+1F425表示小幺鸡表情 ("🐥")当 Unicode 字符串被写进文本文件或其他存储结构当中,这些 unicode 标量将会按照 Unicode 定义的集中格式之一进行编码。其包括
-UTF-8(以8位代码单元进行编码) 和UTF-16(以16位代码单元进行编码)。字符串的 Unicode 表示
+字符的 unicode 标量是一个唯一的21位数字(和名称),例如U+0061表示小写的拉丁字母A ("a"),U+1F425表示小鸡表情 ("🐥") +当 Unicode 字符串被写进文本文件或其他存储结构当中,这些 unicode 标量将会按照 Unicode 定义的集中格式之一进行编码。其包括
+ +UTF-8(以8位代码单元进行编码) 和UTF-16(以16位代码单元进行编码)。字符串的 Unicode 表示(Unicode Representations of Strings)
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示。
-您可以利用
+for-in来对字符串进行遍历,从而以 Unicode 字符的方式访问每一个字符值。 -该过程在 Working with Characters 中进行了描述。您可以利用
for-in来对字符串进行遍历,从而以 Unicode 字符的方式访问每一个字符值。 +该过程在 使用字符 中进行了描述。另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
-
-- UTF-8 代码单元集合 (利用字符串的
-utf8属性进行访问)- UTF-16 代码单元集合 (利用字符串的
-utf16属性进行访问)- 21位的 Unicode 标量值集合 (利用字符串的
+unicodeScalars属性进行访问)- UTF-8 代码单元集合 (利用字符串的
+utf8属性进行访问)- UTF-16 代码单元集合 (利用字符串的
+utf16属性进行访问)- 21位的 Unicode 标量值集合 (利用字符串的
unicodeScalars属性进行访问)下面由
-Dog!和🐶(DOG FACE,Unicode 标量为U+1F436)组成的字符串中的每一个字符代表着一种不同的表示:let dogString = "Dog!🐶" -UTF-8
-您可以通过遍历字符串的
-utf8属性来访问它的UTF-8表示。 -其为 UTF8View 类型的属性,UTF8View 是无符号8位 (UInt8) 值的集合,每一个UInt8值都是一个字符的 UTF-8 表示:+for codeUnit in dogString.utf8 { +下面由
+D``o``g``!和🐶(DOG FACE,Unicode 标量为U+1F436)组成的字符串中的每一个字符代表着一种不同的表示:+ +let dogString = "Dog!🐶" +UTF-8
+您可以通过遍历字符串的
+utf8属性来访问它的UTF-8表示。 +其为UTF8View类型的属性,UTF8View是无符号8位 (UInt8) 值的集合,每一个UInt8值都是一个字符的 UTF-8 表示:for codeUnit in dogString.utf8 { print("\(codeUnit) ") } print("\n") // 68 111 103 33 240 159 144 182 -上面的例子中,前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符
-Dog和!,他们的 UTF-8 表示与 ASCII 表示相同。 -后四个代码单元值 (240, 159, 144, 182) 是DOG FACE的4位 UTF-8 表示。UTF-16
-您可以通过遍历字符串的
-utf16属性来访问它的UTF-16表示。 -其为 UTF16View 类型的属性,UTF16View 是无符号16位 (UInt16) 值的集合,每一个UInt16都是一个字符的 UTF-16 表示:+for codeUnit in dogString.utf16 { +上面的例子中,前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符
+ +Dog和!,它们的 UTF-8 表示与 ASCII 表示相同。 +后四个代码单元值 (240, 159, 144, 182) 是DOG FACE的4字节 UTF-8 表示。UTF-16
+您可以通过遍历字符串的
+utf16属性来访问它的UTF-16表示。 +其为UTF16View类型的属性,UTF16View是无符号16位 (UInt16) 值的集合,每一个UInt16都是一个字符的 UTF-16 表示:for codeUnit in dogString.utf16 { print("\(codeUnit) ") } print("\n") // 68 111 103 33 55357 56374 -同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
-Dog和!,他们的 UTF-16 代码单元和 UTF-8 完全相同。第五和第六个代码单元值 (55357 and 56374) 是
-DOG FACE字符的UTF-16 表示。 -第一个值为U+D83D(十进制值为 55357),第二个值为U+DC36(十进制值为 56374)。Unicode 标量 (Scalars)
-您可以通过遍历字符串的
-unicodeScalars属性来访问它的 Unicode 标量表示。 -其为 UnicodeScalarView 类型的属性, UnicodeScalarView 是UnicodeScalar的集合。 -UnicodeScalar是21位的 Unicode 代码点。每一个
-UnicodeScalar拥有一个值属性,可以返回对应的21位数值,用UInt32来表示。+for scalar in dogString.unicodeScalars { +同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
+Dog和!,它们的 UTF-16 代码单元和 UTF-8 完全相同。第五和第六个代码单元值 (55357 和 56374) 是
+ +DOG FACE字符的UTF-16 表示。 +第一个值为U+D83D(十进制值为 55357),第二个值为U+DC36(十进制值为 56374)。Unicode 标量 (Unicode Scalars)
+您可以通过遍历字符串的
+unicodeScalars属性来访问它的 Unicode 标量表示。 +其为UnicodeScalarView类型的属性,UnicodeScalarView是UnicodeScalar的集合。 +UnicodeScalar是21位的 Unicode 代码点。每一个
+UnicodeScalar拥有一个值属性,可以返回对应的21位数值,用UInt32来表示。for scalar in dogString.unicodeScalars { print("\(scalar.value) ") } print("\n") // 68 111 103 33 128054 -同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
Dog和!。 +同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
-Dog和!。 第五位数值,128054,是一个十六进制1F436的十进制表示。 -其等同于DOG FACE的Unicode 标量 U+1F436。作为查询字符值属性的一种替代方法,每个
-UnicodeScalar值也可以用来构建一个新的字符串值,比如在字符串插值中使用:for scalar in dogString.unicodeScalars { +其等同于DOG FACE的Unicode 标量 U+1F436。 +作为查询字符值属性的一种替代方法,每个
+UnicodeScalar值也可以用来构建一个新的字符串值,比如在字符串插值中使用:for scalar in dogString.unicodeScalars { println("\(scalar) ") } // D @@ -867,7 +902,7 @@ print("\n") // g // ! // 🐶 -