Swift学习笔记(二)--字符串,集合类型与流控制

字符串和字符(Strings and Characters)

  1. 在新版Swift中, 对String进行了本质性的修改, 之前String是字符的集合, 所以, 那个时候可以这样遍历字符串:
for c in "hello" {
  print(c)
}

现在还这么玩就要报错了, 原来是字符数组被放入了一个叫characters的成员变量中, 所以最新的遍历姿势是

for c in "hello".characters {
  print(c)
}
// 同样的, String的长度一般由"hello".characters.count来获取
  1. 可变数组:
    正如笔记一里面说的, 如果String用var来声明, 就是可变的, 在Swift里面, 数组的追加更加方便, 应该是得益于操作符重载的功劳, 如:
var variableString = "Horse"
variableString += " and carriage" // variableString变为"Horse and carriage"
  1. 字符串是值类型:
    所谓值类型就是赋值的时候会copy一遍, 赋值一次多一份, 与之对应的是引用类型, 只copy地址, 赋值多少次都只有一份. 在Swift里面, String, Array, Dictionary和Set都是值类型, 这也就是为什么说Swift安全的原因之二.

  2. 字符串与字符数组的转换:
    二者之前的转换非常自然, String有characters返回字符数组, 而字符数组可以通过String的初始化函数转换为String, 如:

let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
  1. Unicode编码:
    这一块内容很多, 值得了解, 属于重要的内容但是用的时候不会太多, 可以到时候遇到问题再来翻翻文档即可.

  2. 字符串索引
    关于这块内容, 一开始看的时候觉得苹果设计的很奇怪, 先看例子吧:

let greeting = "Guten Tag!"
greeting[greeting.startIndex]   // startIndex肯定是0啦
// G
greeting[greeting.endIndex.predecessor()]  // endIndex是最后的index+1, 这点会让人比较困惑, 为什么不是最后的index呢
// !
greeting[greeting.startIndex.successor()] // successor从当前往后移一位
// u
let index = greeting.startIndex.advancedBy(7) // 往前移7位
greeting[index]
// 所以下面这样导致runtime error的:
greeting[greeting.endIndex] // error
greeting.endIndex.successor() // error

一般情况下我们要对String的字符进行访问, 会直接写index, 但是这样会比较危险, 说不定就越界了, 所以苹果专门有个Index的类型, 还有一些操作的函数来辅助

字符数组有个indices的属性, 所以它的的访问方式还可以是这样的:

for index in greeting.characters.indices {
    print("\(greeting[index]) ")
}
  1. 插入和删除
    如果理解了上面的Index, 那么插入就很常规了, 可以插入字符, 字符串和字符数组, 主要还是要操作好index

  2. 比较字符串
    仍然得益于操作符重载, 现在可以直接==来判断2个字符串相等了, 例如:

let greeting = "hello"
//2016-3-17更正:
//let aGreeting = "hell"+"o"  // 这么写两个常量实际指向同一份数据, 
var aGreeting = "hell"
aGreeting.appendContentsOf("o") // 这么写才能保证指向不同的数据

for index in greeting.characters.indices {
    print("\(greeting[index])", separator: "|", terminator: ".")
}
if greeting == aGreeting {
    print("OK")
}
// 用unsafeAddressOf(greeting)和unsafeAddressOf(aGreeting)可以看到2个变量的地址, 发现并不一样, 说明的确是进行了字符串内容的比较, 而不是比较地址
// 2016-3-17 更正
上面用unsafeAddressOf()函数获取地址的方法貌似并不准确, 关于String的地址, 目前可以通过str._core._baseAddress来观察, Xcode的Debug查看变量也能看到, 这样最直观.

值得一说的是, 因为String里面有Unicode的原因, 所以对Unicode字符的比较会更加科学, 具体的例子参考官方文档看字符串比较的那一大段

集合类型

数组, 字典和集合(Set)都是集合类型, 在Swift中CollectionType是一个协议, 只要你实现了这个协议, 都算集合, 并且如前文所述, 这些集合都是值类型.
这些集合都支持通用类型(generic types), 可以理解为C++的模板. 在ObjC的最新加的三大特性之一也是这个.

以Array为例子, Set和Dictionary基本上都差不多, 有特别的会单独点出来.

  1. 声明方式:
var someInts = [Int]() // 貌似以前不是这么玩的
print("someInts is of type [Int] with \(someInts.count) items.")
var threeDoubles = [Double](count: 3, repeatedValue: 0.0)// 默认值的方式
var shoppingList: [String] = ["Eggs", "Milk"]

// 字典声明也差不多:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
  1. 操作:
    追加什么的都比较常规, 直接看一些得益于操作符重载的用法吧:
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// 注意不能直接 += "Apple"

比较有特色的玩法是:

shoppingList[4...6] = ["Bananas", "Apples"]//把index为4,5,6的替换为后面的数组, 但是前面是3个元素, 后面是2个, 多出的那个呢? 被丢掉了...
// 注意: 左边多右边少是可以的, 反过来就错了

Dictionary里面用下标赋值为nil是不会crash的, 与Objc里面setValue:forKey:一样会移除原有值(如有), 但是, 调用updateValue 设置为nil则会crash, 同时, Dictionary里面取出的值是Optional的, 记得拆包再用

  1. 遍历:
    和之前字符串那个差不多, 看一下元组在这里的应用:

for (index, value) in shoppingList.enumerate() {
print("Item (index + 1): (value)")
}
// cmd+单击可以看到enumerate()返回一个EnumerateSequence对象, 再点进去是一个结构体, 发现有一个next函数, 是这样的
//func next() -> (index: Int, element: Base.Element)?
这是所有可遍历对象需要实现的方法, 返回的就是一个元素, 包含index和element

// 同样Dictionary也可以这么遍历:
for (airportCode, airportName) in airports {
print("(airportCode): (airportName)")
}
// 如果想单独遍历key或者value, .keys和.values可以拯救


4. 排序:
提供了sort(), 调用即可:

var numberArray = [3,1,4,8,2]
numberArray.sort() // 默认升序

当然还可以自己写, 然后传给sort, 这里牵涉到闭包, 会有很多写法, 最简单的是:

numberArray.sort(>) // 降序排列


4. 集合关系
intersect: 2个集合的交集
union: 2个集合的并集
exclusiveOr: 2个集合并集减去交集
subtract: 集合减去与另一集合的交集
官方图:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setVennDiagram_2x.png


简单介绍了下集合类型, 其实里面包含了不少东西, 也牵涉到了苹果对于Swift的设计理念--面向协议编程, 和闭包的东西. 暂时先介绍到这里, 更进一步的可以看看[官方文档](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-ID105)


###流程控制

1. for循环:
之前已经讲过一些了, 比如:

for i in 1...5 {
print("(index) times 5 is (index * 5)")
}
//比较有意思的是下划线(_)在这里的使用
for _ in 1...5 {
// do something five times
}
// 用下划线代表这个值我不需要

// 当然也保留了传统的方式, 只是会用的人应该会很少了
for var index = 0; index < 3; ++index {
print("index is (index)")
}


2. while循环:
和别的语言差不多, 有区别的是引入了一个repeat, 替换掉了do-while, 因为do因为用在do-try-catch里面去了, 解释好牵强...

3. if条件语句
基本上也差不多, 只是不需要加括号了, 同时判断的值必须要是实现了BooleanType协议的, 不然会报错, 例如:

var i = 1
if i { // 会报错, 因为Int没有实现BooleanType
// print("i is not 0")
}


4. switch:
switch在Swift得到了大量的增强, 因为switch涉及到模式匹配, 而Swift的模式匹配十分强大.
4.1 switch不仅仅匹配整数, 还可以匹配字符,字符串, 严格说来是任何只要实现了Equatable协议的对象
4.2 switch的case默认自带break, 但是如果这一case一行语句都没有, 是需要手动加上break的. 同时如果你想让这个case不自动break, 就要加上关键字fallthrough, 
4.3 范围匹配, 以官网例子说明:

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are (naturalCount) (countedThings).")

值得说明的是, 范围必须包含条件的全部可能的范围, 简单来说就是, 如果不是枚举, 就把default写上
4.4 元组在switch里面也得到了很广的使用, 还是用例子来说明:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(0, 0) is at the origin")
case (_, 0): // 下划线代表任意值都可以匹配上
print("((somePoint.0), 0) is on the x-axis")
case (0, _):
print("(0, (somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
print("((somePoint.0), (somePoint.1)) is inside the box")
default:
print("((somePoint.0), (somePoint.1)) is outside of the box")
}

4.5 值绑定, 依然例子:

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of (x)")
case (0, let y):
print("on the y-axis with a y value of (y)")
case let (x, y):
print("somewhere else at ((x), (y))")
}
// 同时还可以加条件判断, 不过是用where:
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("((x), (y)) is on the line x == y")
case let (x, y) where x == -y:
print("((x), (y)) is on the line x == -y")
case let (x, y):
print("((x), (y)) is just some arbitrary point")
}

5. 流程跳转:
其实也就是break, continue, fallthrough, return和throw, 这些关键字都可能打破当前的流程, 跳到别的流程中去, fallthrough已经介绍过, 其余的都和别的语言一致

6. 标签:
与C语言的标签可以跳来跳去不通, Swift的标签配合循环使用, 个人感觉用的范围应该不多, 有兴趣的在官网看下例子就好.

7. guard:
Swift2引入了一个新的关键字, guard, 可以在函数中对参数进行一些判断, 以官网例子来看代码:

func greet(person: [String: String]) {
guard let name = person["name"] else { // 必须保证有name, 否则就返回
return
}
print("Hello (name)!")
guard let location = person["location"] else { // 必须保证有location, 否则就返回
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in (location).")
}

如果一定要问guard和if的区别是什么, 那么直接援引一个例子来感受一下吧(更多guard优于if的例子请[查看原文](http://swift.gg/2015/08/06/swift-guard-better-than-if/)):

struct Person {
let name: String
var age: Int
}

func createPerson() throws -> Person {
guard let age = age, let name = name
where name.characters.count > 0 && age.characters.count > 0 else {
throw InputError.InputMissing
}

    guard let ageFormatted = Int(age) else {
        throw InputError.AgeIncorrect
    }

    return Person(name: name, age: ageFormatted)
}

// 如果用if来写, 会是这样的:
func createPersonNoGuard() -> Person? {
if let age = age, let name = name
where name.characters.count > 0 && age.characters.count > 0
{
if let ageFormatted = Int(age) {
return Person(name: name, age: ageFormatted)
} else {
return nil
}
} else {
return nil
}
}
需要不停地if-let, 还要写 else {return nil}, 如果再多一点属性需要判断, 就更多的if-let和else{return nil}...


8. 检查版本可用性

这个是Swift2带来的大杀器, 在iOS或者OSX版本更迭中, 总有一些新的API出现, 也有旧的API废弃, 如果你的APP需要兼容多个系统版本, 那么势必要为这些API进行甄别, 但是这样的成本过高, 必须要对这些API什么时候引入, 什么时候废除有谱, 或者每次去查看, 这都对APP出现unrecognized selector异常埋下隐患, 所以这是Swift安全的原因之三, 同时, 如果你写了一些API是高于你在工程中设置的最低版本的话, 编译器是会给你报错, 强制你去判断的, 我觉得这点非常人性化. 判断的具体写法是这样的:

if #available(iOS 9, OSX 10.10, ) {// 最后的这个是通配符, 主要是为以后拓展做的准备, 假如将来来了一个CarOS或者HouseOS, 那么就能匹配上了
// Use iOS 9 APIs on iOS, and use OS X v10.10 APIs on OS X
} else {
// Fall back to earlier iOS and OS X APIs
}


同时, 如果你的某个类或者某个函数使用了高版本的API, 那么你就要在前面写上@available, 这样编译器就会为你做检查了:

@available(iOS 9.2, OSX 10.10.3, *)
class MyClass {
// class definition
}


简单介绍到这里, 这一篇就算结束了, 前面基本算都是基础知识, 惯例, 细节部分参考[官方文档](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID120)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容