Swift的基本知识
基础部分的知识本文并没有很详细的讲解,只是在学习过程中觉得应该注意的点,详细的基础教程建议查看SwiftGG
Swift常量和变量的定义:
使用let定义常量,var定义变量。
let pi = 3.14159
var enemyKilled = 100
和Java一样常量定义时必须赋值,但Swift可以不指定数据类型,编译器会根据常量的值,自动判断常量的类型。上面的代码,编译器会认为pi是常量是浮点数。一旦定义了一个常量或变量,将不可以再改变它们的数据类型。
为了能够直观地显示常量或变量的数据类型,可以在定义时通过冒号设置它们的数据类型。
var age : Int = 24
基础数据类型和Java相似,包含整数Int,浮点数Double、Float,布尔值Bool以及String字符串,另外Swift还提供了两种强大的集合数据类型:数组Array和字典Dictionary,和Java的List和Map很相似,但也有一些区别,后面数据类型在详讲。
变量名的定义与Java一样,包含_、数字、字母,但不能以数字开头,不能是关键字。另外Swift还支持Unicode字符作为变量名。
let _count=6
let Count=8
let 猫="Cat"
注释:
与Java相似
doSomething() //单行注释
/*这是一个
多行注释
可以写更多的内容*/
doSomething()
数据类型和运算
基础数据类型和Java没什么差别
- Bool(布尔):类型只能是true或false.
- Int(整型):没有小数的数字,UInt8代表8位无符号整数a,Int32代表32位有符号整数,大多数情况不需要指定特定的大小。Swift提供的Int会根据应用程序的运行环境自动调节大小。
- 浮点类型:Double 表示64位浮点数 Float 表示32位浮点数
- Character(字符)和String(字符串):字符串拼接使用\()或+
let age = 20 let str = "年龄\(age)" let str1 = "年龄"+age
字符串的常用操作
var hello = "Hello world" /* 字符串比较 */ hello == "Hello world" //true 字符串比较 hello.hasPrefix("Hello") // true 判断以字符串开始 hello.hasSuffix("world") // true 判断以字符串结尾 /* 字符串大小写转换 */ hello.uppercased() //HELLO WORLD 转大写 hello.lowercased() //hello world 转小写 hello.capitalized //Hello World首字母大写 /* 字符串截取 */ hello.substring(to: hello.index(hello.endIndex,offsetBy:-8)) //Hel hello.replacingOccurrences(of: "Hello", with: "Hi") //Hi world hello.remove(at: hello.index(hello.startIndex,offsetBy:2)) //l
元组类型
元组是包含了若干相关联变量的对象
let people = ("John",33,"China")// people被推导为(String,int,String)类型的常量。
print("name is \(people.0)") //输出:name is John
let httpStatus = (statusCode:200,description:"OK")
print("status code is \(httpStatus.statusCode)")//输出:status code is 200
元组做为一个临时复合值,常被用作函数的返回值,用来传递一些数据。如果你的数据结构不是临时的,还是建议使用类或者结构体。
可选值
使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。可选值申明在类型后加?,当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!
)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):
var serverResponseCode: Int? = 404
let code:Int = serverResponseCode!
如果可选类型不包含值,强制解析会报错。
你可以使用 if
语句和 nil
比较来判断一个可选值是否包含值。你可以使用“相等”(==
)或“不等”(!=
)来执行比较。
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 输出“convertedNumber contains some integer value.”
nil
你可以给可选变量赋值为 nil
来表示它没有值:
var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
serverResponseCode = nil
// serverResponseCode 现在不包含值
注意
nil
不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil
:
var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
隐式解析可选类型
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?
)改成感叹号(String!
)来声明一个隐式解析可选类型。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 String
和隐式解析可选类型 String
之间的区别:
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
注意:如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
运算符
swift的一元、二元、三元运算符与Java、C等语言没太大区别。主要有一下几点差异
Swift 的赋值操作并不返回任何值,避免把 (==
)错写成(=
)这类错误的出现
if x = y {
// 此句错误,因为 x = y 并不返回任何值
}
Swift 也提供恒等(===
)和不恒等(!==
)这两个比较符来判断两个对象是否引用同一个对象实例。如果你学过JavaScript,那对恒等和不恒等运算符很熟悉。
Swift 提供空合运算符(a ?? b
)将对可选类型 a
进行空判断,如果 a
包含一个值就进行解包,否则就返回一个默认值 b
。表达式 a
必须是 Optional 类型。默认值 b
的类型必须要和 a
存储值的类型保持一致。
let defaultColorName = "red"
var userDefinedColorName: String? //默认值为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
区间运算符:Swift 提供了几种方便表达一个区间的值的区间运算符。
- 闭区间运算符(
a...b
)定义一个包含从a
到b
(包括a
和b
)的所有值的区间。a
的值不能超过b
。闭区间运算符在迭代一个区间的所有值时是非常有用的,如在for-in
循环中:for index in 1...5 { print("\(index) * 5 = \(index * 5)") } // 1 * 5 = 5 // 2 * 5 = 10 // 3 * 5 = 15 // 4 * 5 = 20 // 5 * 5 = 25
-
半开区间运算符(
a..<b
)定义一个从a
到b
但不包括b
的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。半开区间的实用性在于当你使用一个从 0 开始的列表(如数组)时,非常方便地从0数到列表的长度。let names = ["Anna", "Alex", "Brian", "Jack"] let count = names.count for i in 0..<count { print("第 \(i + 1) 个人叫 \(names[i])") } // 第 1 个人叫 Anna // 第 2 个人叫 Alex // 第 3 个人叫 Brian // 第 4 个人叫 Jack
数组有 4 个元素,但
0..<count
只数到3(最后一个元素的下标),因为它是半开区间。 -
单侧区间,闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间 —— 例如,一个包含了数组从索引 2 到结尾的所有值的区间。在这些情况下,你可以省略掉区间操作符一侧的值。这种区间叫做单侧区间,因为操作符只有一侧有值。例如:
for name in names[2...] { print(name) } // Brian // Jack for name in names[...2] { print(name) } // Anna // Alex // Brian
半开区间操作符也有单侧表达形式,附带上它的最终值。就像你使用区间去包含一个值,最终值并不会落在区间内。例如:
for name in names[..<2] { print(name) } // Anna // Alex
单侧区间不止可以在下标里使用,也可以在别的情境下使用。你不能遍历省略了初始值的单侧区间,因为遍历的开端并不明显。你可以遍历一个省略最终值的单侧区间;然而,由于这种区间无限延伸的特性,请保证你在循环里有一个结束循环的分支。你也可以查看一个单侧区间是否包含某个特定的值,就像下面展示的那样。
let range = ...5 range.contains(7) // false range.contains(4) // true range.contains(-1) // true
字符和字符串
多行字符串字面量:
如果你需要一个字符串是跨越多行的,那就使用多行字符串字面量 — 由一对三个双引号包裹着的具有固定顺序的文本字符集
let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
字符、字符串拼接:
可通过+、+=、append()进行字符拼接
let string1 = "hello"
var string2 = " there"
var welcome = string1 + string2
string2+=string1
string2.append(string1)
不同于Java中String的不可变,Swift中String是可变的,可以通过append进行拼接,前提是定义的变量,let定义的String常量,不能修改。
比较字符串:
字符串/字符可以用等于操作符(==
)和不等于操作符(!=
),功能类似Java中String.equal(),比较的是值想等,如果想比较是否是同一个实例,同通过恒等操作符(===)和恒不等操作符(!==)
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
print("These two strings are considered equal")
}
// 打印输出“These two strings are considered equal”
前缀/后缀相等,通过调用字符串的 hasPrefix(_:)
/hasSuffix(_:)
方法来检查字符串是否拥有特定前缀/后缀,两个方法均接收一个 String
类型的参数,并返回一个布尔值。
集合类型
Swift 语言提供 Arrays
、Sets
和 Dictionaries
三种基本的集合类型用来存储集合数据。数组(Arrays)是有序数据的集。集合(Sets)是无序无重复数据的集。字典(Dictionaries)是无序的键值对的集。
Swift 语言中的 Arrays
、Sets
和 Dictionaries
中存储的数据值类型必须明确。这意味着我们不能把错误的数据类型插入其中。同时这也说明你完全可以对取回值的类型非常放心。
Arrays
、Sets
和 Dictionaries
三种集合都是值类型,分配给变量,在创建后可以添加更多或移除已存在的的数据项。如果分配给常量,那它就是不可变的,它的大小和内容都不能被改变。
数组(Arrays)
数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
创建一个数组:
let stringArray = Array<String>()
let floatArray = [Float]()
let intArray = [1,2,3,4,5]
let 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]
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。
访问和修改数组:
可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
//使用数组的只读属性 count 来获取数组中的数据项数量.
print("The shopping list contains \(shoppingList.count) items.")//输出“The shopping list contains 2 items.”(这个数组有2个项)
//使用布尔属性 isEmpty 作为一个缩写形式去检查 count 属性是否为 0
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// 打印“The shopping list is not empty.”(shoppinglist 不是空的)
//使用 append(_:) 方法在数组后面添加新的数据项
shoppingList.append("Flour")
// shoppingList 现在有3个数据项,有人在摊煎饼
//使用加法赋值运算符(+=)也可以直接在数组后面添加一个或多个拥有相同类型的数据项
shoppingList += ["Baking Powder"]
// shoppingList 现在有四项了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 现在有七项了
//直接使用下标语法来获取数组中的数据项
var firstItem = shoppingList[0]
// 第一项是“Eggs”
//注意:第一项在数组中的索引值是 0 而不是 1。 Swift 中的数组索引总是从零开始。
//可以用下标来改变某个已有索引值对应的数据值:
shoppingList[0] = "Six eggs"
// 其中的第一项现在是“Six eggs”而不是“Eggs”
//可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有6项
//调用数组的 insert(_:at:) 方法来在某个具体索引值之前添加数据项
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 现在有7项
// 现在是这个列表中的第一项是“Maple Syrup”
//类似的我们可以使用 remove(at:) 方法来移除数组中的某一项
let mapleSyrup = shoppingList.remove(at: 0)
// 索引值为0的数据项被移除
// shoppingList 现在只有6项,而且不包括 Maple Syrup
// mapleSyrup 常量的值等于被移除数据项“Maple Syrup”的值
//可以使用 for-in 循环来遍历所有数组中的数据项
for item in shoppingList {
print(item)
}
集合(Sets)
集合(Set)用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
创建一个数组:
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// 打印“letters is of type Set<Character> with 0 items.”
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被构造成含有三个初始值的集合
访问和修改一个集合:
//找出一个 Set 中元素的数量,可以使用其只读属性 count
print("I have \(favoriteGenres.count) favorite music genres.")
// 打印“I have 3 favorite music genres.”
//使用布尔属性 isEmpty 作为一个缩写形式去检查 count 属性是否为 0
if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// 打印“I have particular music preferences.”
//可以通过调用 Set 的 insert(_:) 方法来添加一个新元素:
favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素
//remove(_:) 方法去删除一个元素.并且返回被删除的元素值
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// 打印“Rock? I'm over it.”
//通过 removeAll() 方法删除Set 中的所有元素
favoriteGenres.removeAll()
//删除了favoriteGenres中所有的元素
//contains(_:) 方法去检查 Set 中是否包含一个特定的值
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// 打印“It's too funky in here.”
//for-in 循环中遍历一个 Set 中的所有值
for genre in favoriteGenres {
print("\(genre)")
}
集合操作:你可以高效地完成 Set
的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
基本集合操作
下面的插图描述了两个集合 a
和 b
,以及通过阴影部分的区域显示集合各种操作的结果。
-
使用
intersection(_:)
方法根据两个集合中都包含的值创建的一个新的集合。 -
使用
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
的父集合,因为 a
包含了 b
中所有的元素,相反的,集合 b
是集合 a
的子集合,因为属于 b
的元素也被 a
包含。集合 b
和集合 c
彼此不关联,因为它们之间没有共同的元素。
-
使用“是否相等”运算符(
==
)来判断两个集合是否包含全部相同的值。 -
使用
isSubset(of:)
方法来判断一个集合中的值是否也被包含在另外一个集合中。 -
使用
isSuperset(of:)
方法来判断一个集合中包含另一个集合中所有的值。 -
使用
isStrictSubset(of:)
或者isStrictSuperset(of:)
方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。 -
使用
isDisjoint(with:)
方法来判断两个集合是否不含有相同的值(是否没有交集)。
let houseAnimals: Set = ["?", "?"]
let farmAnimals: Set = ["?", "?", "?", "?", "?"]
let cityAnimals: Set = ["?", "?"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
字典(Dictionaries
)
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
创建一个字典:
var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//使用字面量创建[String: String]字典
访问和修改字典:
//和数组一样,我们可以通过字典的只读属性 count 来获取某个字典的数据项数量
print("The dictionary of airports contains \(airports.count) items.")
// 打印“The dictionary of airports contains 2 items.”(这个字典有两个数据项)
//使用布尔属性 isEmpty 作为一个缩写形式去检查 count 属性是否为 0
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary is not empty.")
}
// 打印“The airports dictionary is not empty.”
//可以使用一个恰当类型的键作为下标索引,并且分配恰当类型的新值:
airports["LHR"] = "London"
// airports 字典现在有三个数据项
//字典的 updateValue(_:forKey:) 方法可以设置或者更新特定键对应的值,并且返回对应值的类型的可选值,如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是 nil。
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:) 方法也可以用来在字典中移除键值对
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// 打印“The removed airport's name is Dublin Airport.”
//for-in 循环来遍历某个字典中的键值对
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow
//通过访问 keys 或者 values 属性,我们也可以遍历字典的键或者值
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow