swift语法基础(三)

集合类型

swift使用数组,集合,字典三种基本的集合类型用来存储集合数据,数组是有序数据的集,集合是无序数据的集,字典是无序的键值对的集。

swift中的数组,集合和字典必须明确其中保存的键和值类型,这样可以防止插入数据类型不一致。

  • 集合的可变性

如果创建一个数组,集合或者字典并且把它分配成一个变量,那么这个集合将会是可变的。可以进行增删数据。如果是常量则它的大小和内容都不可变。

数组

数组中相同的值可以出现在一个数组不同位置多次。简单语法为: Array < Element>,Element是这个数组中允许的数据类型,可以使用简单语法[Element]。

  • 创建空数组

使用下面语法创建一个空数组:

var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印“someInts is of type [Int] with 0 items.”

如果代码中提供了类型信息,可以使用空数组语句将一个数组改为空数组

someInts.append(3)
// someInts 现在包含一个 Int 值
someInts = []
// someInts 现在是空数组,但是仍然是 [Int] 类型的。
  • 创建一个带有默认值的数组

swift中可以创建特定大小并且所有数据都被默认构造的方法。如下:

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0]
  • 两个数组相加创建一个数组

可以使用+来组合两个已经存在的相同类型数组,

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]

var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
  • 使用数组字面量构造数组

可以使用数组字面量构造数组,这与C语言类似:

var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。

//由于swift的类型推断机制 ,可以这样写:
var shoppingList = ["Eggs", "Milk"]
  • 访问和修改数组

可以通过数组的方法和属性来访问和修改数组,或者使用下标

//使用count属性获取数组数据项数量    
print("The shopping list contains \(shoppingList.count) items.")
// 输出“The shopping list contains 2 items.”(这个数组有2个项)

使用isEmpty属性检查数组是否为空,使用append(_:)方法哎数组后面增加数据项:

shoppingList.append("Flour")
// shoppingList 现在有3个数据项

使用+= + 属性进行同类型数组的数据增加

shoppingList += ["Baking Powder"]
// shoppingList 现在有四项了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 现在有七项了

使用下标语法获取数据项,下标从0开始。注意下标不能溢出。

还可以使用下标改变一系列的值,即使更新前后的数据量不同,也正确

shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有6项
//将三个数据替换为两个数据

使用insert(_:at:)方法在某个指定索引值之前添加数据项

shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 现在有7项
// 现在是这个列表中的第一项是“Maple Syrup”

使用remover(at:)方法溢出数组中某一项,并且返回被溢出的数据项

let mapleSyrup = shoppingList.remove(at: 0)
// 索引值为0的数据项被移除

值得注意的是,数据被溢出后,空出项会被自动填补。

如果通过是需要每个数据项的值和索引值,使用enumerated()方法遍历数组,enumerated()返回一个由索引值和数据值组成的元组数组。索引下标从0开始:

for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas

集合

集合用来存储相同类型并且没有确定顺序的值,**当集合元素顺序不重要时或者希望确保每个元素只出现依次时可以使用集合。

  • 集合类型的hash值

一个类型为了存储在集合中,该类型必须是可hash化的,即该类型必须提供一个方法来计算它的哈希值,哈希值类型是Int,相等的对象hash值必须相同,比如a==b,因此必须使用a.hashvalue==b.hashvalue. swift中所有基本类型都是可hash化的。可以作为集合值得类型或者字典键的类型。没有关联值得枚举成员值默认也是可hash化的。

  • 集合类型语法

集合类型被写为Set< Element>,这里Element表示集合中允许存储的类型。

  • 创建一个空的集合

可以通过构造器创建一个特定类型的空集合

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// 打印“letters is of type Set<Character> with 0 items.”

可以通过上下文提供的类型信息,将一个集合设置为空的集合。

letters.insert("a")
// letters 现在含有1个 Character 类型的值
letters = []
// letters 现在是一个空的 Set,但是它依然是 Set<Character> 类型
  • 使用数组字面量创建集合

使用数组字面量来构造集合,与c语言类似

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被构造成含有三个初始值的集合

由于swift的推断功能,可以无须写出集合类型,根据成员推断出

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
  • 访问和修改集合

可以通过集合的属性和方法进行访问和修改

使用count属性获取集合元素数量。

使用isEmpty属性检查集合是否为空

使用insert(_:)插入一个新元素。

favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素

使用remove属性删除一个元素,如果是该元素存在则返回删除的元素,否则返回nil。可以通过removeAll()方法删除所有元素。

let removedGenre = favoriteGenres.remove("Rock")

使用contains(_:)方法检察一个集合中是否包含特定的值。返回一个bool值。

可以在for-in循环中遍历一个集合所有成员:

for genre in favoriteGenres {
    print("\(genre)")
}

notice:swift中Set类型没有确定顺序,为了保证返回一个有序数组,可以使用sorted()方法遍历集合,将元素按字典序排列后遍历。

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical
// Hip hop
// Jazz
  • 集合操作

可以使用集合的特性完成一些操作。比如判断两个集合是否全包含,部分包含或者不相交。

下图描述了集合a和b,以及通过阴影部分显示集合各种操作的结果。

set

如上图:

使用inertsection(_:)方法根据两个集合的交集创建一个新的集合。

使用 symmetricDifference(_:) 方法根据两个集合不相交的值创建一个新的集合。

使用union(_:)方法根据两个集合的所有值创建一个新的集合。

使用subtracting(_:)方法根据不再一个集合中的值创建一个新的集合。

let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
  • 集合成员关系和相等

下图描述了三个集合a,b,c以及通过重叠区域表述集合间共享的元素,a是b的父集合,b是a的子集合。b和c是不相交集合。

set2

如上图:
使用 == 判断两个集合包含的值是否相同

使用isSubset(of:)方法判断一个集合中的所有值是否也被包含在另外一个集合中。

使用isSuperset(of:)方法判断一个集合是否包含另外一个集合所有值。

使用isStrictSubset(of:)或者isStrictSuperset(of:)方法判断一个集合是否是另外一个集合子集合或者父集合并且两个集合并不相等。

let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

字典

字典是一种无序的集合,存储的是键值对间的关系,所有键和值的类型需要相同。一个key对应一个value。

  • 字典简单语法

使用Dictionary<key,value>定义。也可以使用 [key:value]这样的简化定义。

  • 创建一个空字典

使用类似数组的方式创建一个空字典

var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典

如果上下文已经提供了类型信息,可以使用字面量来创建一个空字典,记作[:]

namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
  • 使用字典字面量创建字典

这个方法与之前创建数组类似:

var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//简化版
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
  • 访问和修改字典

使用只读的count属性获取字典的数据项数量

使用isEmpty属性检查字典是否为空

使用下标语法给字典增加新的数据项。

airports["LHR"] = "London"
// airports 字典现在有三个数据项

使用updataeValue(_:forkey:)方法可以更新特定键值对的值。这个方法会返回更新前的value值

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \(oldValue).")
}
// 输出“The old value for DUB was Dublin.”

使用removeValue(forkey:)方法在字典中移除键值对。这个方法在键值对存在时会返回被移除的值或者不存在时返回nil。

  • 字典遍历

使用for-in遍历某个字典,每个数据项都已键值对形式返回,可以使用临时常量或者变量分解这些元组。

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow

//访问key属性

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR

//访问value属性
for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow

如果需要字典的键集合或者值集合。可以使用如下方法构建一个新数组:

let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]

let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]

需要有序遍历字典,可以使用sorted()方法。

控制流

swift提供了多种控制流结构,包括while, if,guard,switch,break,countinue,for-in。

for-in循环

使用for-in遍历集合中所有元素,或者某个范围内元素

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

可以限定范围:

for index in 1...5 {
        print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

如果不需要区间内的每一项,可以使用_忽略:

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// 输出“3 to the power of 10 is 59049”

使用stride(from:through:by),可以遍历某一个间隔

let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // 每3小时渲染一个刻度线(3, 6, 9, 12)
}

while循环和 Repeat-While

while循环,每次在循环开始时计算条件是否满足。

repeat-while循环,每次在循环结束时计算条件是否满足。

if语句和switch语句

swift中的if使用和c语言中一致。

switch尝试把某个值与若干个case匹配i,匹配成功时执行相应代码。执行完相对应的case语句后不需要break可以自行跳出。

notice:每个case语句必须包含至少一条语句。

swift中的case分支可以是一个值区间:

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let 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).")
// 输出“There are dozens of moons orbiting Saturn.”

可以使用元组在同一个switch语句中测试多个值:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint) is at the origin")
case (_, 0):
    print("\(somePoint) is on the x-axis")
case (0, _):
    print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
    print("\(somePoint) is inside the box")
default:
    print("\(somePoint) is outside of the box")
}
// 输出“(1, 1) is inside the box”
  • 值绑定

case分支允许将匹配的值声明为临时变量或者常量,并且允许在case分支内使用。这成为值绑定。

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))")
}
//此处最后一个case可以匹配所有剩余情况    
// 输出“on the x-axis with an x value of 2”
  • where

case分支可以使用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")
}
// 输出“(1, -1) is on the line x == -y”
  • 复合型case

当多个条件可以使用同一种方法来处理时,可以将几种可能放在一个case后面,用逗号隔开。

let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
     "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}
// 输出“e is a vowel”

控制转移语句

  • continue

告诉循环体立刻停止本次循环,开始下一次循环。

  • break

立刻结束整个控制流的执行。

  • Fallthrough
    fallthrough 简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的 switch 语句特性是一样的。

    let integerToDescribe = 5
    var description = “The number (integerToDescribe) is”
    switch integerToDescribe {
    case 2, 3, 5, 7, 11, 13, 17, 19:

    description += " a prime number, and also"
    fallthrough

    default:

    description += " an integer."

    }
    print(description)
    // 输出“The number 5 is a prime number, and also an integer.”

  • 带标签的语句和guard

参考此处:

https://swiftgg.gitbook.io/swift/swift-jiao-cheng/05_control_flow

  • 检测API可用性

swift内置支持API可用性,可以确保我们不会在当前部署机器上使用不可用API。

if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
    // 使用先前版本的 iOS 和 macOS 的 API
}