Swift-基础、集合类型、控制流等
最近项目使用的是OC,后头看之前用Swift开发的一个项目时,发现很多细节都忘记了😭😭。
为了回忆和以后方便查看,现在根据官方文档swift编程语言,做下笔记。
1、 常量和变量必须在使用前声明:
用 let
来声明常量;
用 var
来声明变量。
let maximumNumberOfLoginAttempts = 10
表示:声明一个名字是 maximumNumberOfLoginAttempts 的新常量,并给它一个值 10 。
var currentLoginAttempt = 0
表示:声明一个名字是 currentLoginAttempt 的变量并将它的值初始化为 0 。
2、 类型标注
当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
var welcomeMessage: String
声明一个类型为 String ,名字为 welcomeMessage 的变量。
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型。
3、 输出
Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
var friendlyWelcome = "Hello!"
print("The current value of friendlyWelcome is (friendlyWelcome)")
// 输出 "The current value of friendlyWelcome is Hello!
4、 分号
Swift 并不强制要求你在每条语句的结尾处使用分号(;)当然也可以按照自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:
let cat = "🐱"; print(cat)
// 输出 "🐱"
5、 整数
- Int
一般来说,你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型Int,长度与当前平台的原生字长相同
在32位平台上,Int 和 Int32 长度相同。
在64位平台上,Int 和 Int64 长度相同。
注意
:除非你需要特定长度的整数,一般来说使用 Int 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,Int 可以存储的整数范围也可以达到 -2,147,483,648 ~ 2,147,483,647 ,大多数时候这已经足够大了。
6、类型安全和类型推断
Swift 是类型安全的,它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
- 如果你给一个新常量赋值 42 并且没有标明类型,Swift 可以推断出常量类型是 Int
let meaningOfLife = 42
// meaningOfLife 会被推测为 Int 类型
- 如果你没有给浮点字面量标明类型,Swift 会推断你想要的是 Double:
当推断浮点数的类型时,Swift 总是会选择 Double 而不是Float。
let pi = 3.14159
// pi 会被推测为 Double 类型
- 如果表达式中同时出现了整数和浮点数,会被推断为 Double 类型:
let anotherPi = 3 + 0.14159
// anotherPi 会被推测为 Double 类型
原始值 3 没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为 Double 类型。
7、 元祖
元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型
,并不要求是相同类型。
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")
(404, "Not Found") 元组把一个 Int 值和一个 String 值组合起来表示 HTTP 状态码的两个部分。
这个元组可以被描述为“一个类型为 (Int, String) 的元组”。
- 你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:
let (statusCode, statusMessage) = http404Error
print("The status code is (statusCode)")
// 输出 "The status code is 404"
print("The status message is (statusMessage)")
// 输出 "The status message is Not Found"
- 如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记
let (justTheStatusCode, _) = http404Error
print("The status code is (justTheStatusCode)")
// 输出 "The status code is 404"
*还可以通过下标来访问元组中的单个元素,下标从零开始:
print("The status code is (http404Error.0)")
// 输出 "The status code is 404"
print("The status message is (http404Error.1)")
// 输出 "The status message is Not Found"
- 可以在定义元组的时候给单个元素命名:
let http200Status = (statusCode: 200, description: "OK")
- 给元组中的元素命名后,你可以通过名字来获取这些元素的值:
print("The status code is (http200Status.statusCode)")
// 输出 "The status code is 200"
print("The status message is (http200Status.description)")
// 输出 "The status message is OK"
注意
:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
8、可选类型
在值可能为空的情况下,你可以使用可选类型;可选类型有两种可能性:
1:有值,解析取值
2:没有值
-
下面的例子使用这种构造器来尝试将一个 String 转换成 Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"
因为该构造器可能会失败,所以它返回一个可选类型(optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值。不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都没有。)
-
nil
Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。
你可以给可选变量赋值为nil来表示它没有值:
var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
serverResponseCode = nil
// serverResponseCode 现在不包含值
注意
nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
-
if语句
你可以使用 if 语句和 nil 比较来判断一个可选值是否包含值。你可以使用“相等”(==)或“不等”(!=)来执行比较。
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 输出 "convertedNumber contains some integer value."
-
强制解析
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"
print("convertedNumber has an integer value of (convertedNumber!).")
// 输出 "convertedNumber has an integer value of 123."
-
可选绑定
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
let possibleNumber = "123"
if let actualNumber = Int(possibleNumber) {
print("'(possibleNumber)' has an integer value of (actualNumber)")
} else {
print("'(possibleNumber)' could not be converted to an integer")
}
// 输出 "'123' has an integer value of 123"
这段代码可以被理解为:
如果 Int(possibleNumber) 返回的可选 Int 包含一个值,创建一个叫做 actualNumber 的新常量并将可选包含的值赋给它。
有值的话,actualNumber 常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。
-
隐式解析可选类型
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。这时候把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。
普通的可选类型
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值
隐式解析可选类型
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。
9、区间运算符
- 闭区间运算符
a...b
定义一个包含从 a 到 b(包括 a 和 b)的所有值的区间。a 的值不能超过 b。 - 半开区间运算符
a..<b
定义一个从 a 到 b 但不包括 b 的区间。
10、 字符串
-
Swift 的String类型是值类型。 如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。 任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
11、集合类型
Swift 语言提供Arrays、Sets和Dictionaries三种基本的集合类型用来存储集合数据。
数组(Arrays)是有序数据的集。
集合(Sets)是无序无重复数据的集。
字典(Dictionaries)是无序的键值对的集。
注意
:Swift 的Arrays、Sets和Dictionaries类型被实现为泛型集合。
如果创建一个Arrays、Sets或Dictionaries并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把Arrays、Sets或Dictionaries分配成常量,那么它就是不可变的,它的大小和内容都不能被改变。
-
数组(Arrays)
数组使用有序
列表存储同一类型
的多个值。相同的值可以多次出现在一个数组的不同位置中。
1、 创建一个空数组
var someInts = [Int] ()
print("someInts is of type [Int] with (someInts.count) items.")
// 打印 "someInts is of type [Int] with 0 items."
someInts的值类型被推断为[Int]
2、创建一个带有默认值的数组
Swift 中的Array类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(count)和适当类型的初始值(repeating)传入数组构造函数:
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0]
3、通过两个数组相加创建一个数组
我们可以使用加法操作符(+)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
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]
4、用数组字面量构造数组
数组字面量是一系列由逗号分割并由方括号包含的数值
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。
还可以等价于
var shoppingList = ["Eggs", "Milk"]
因为所有数组字面量中的值都是相同的类型,Swift 可以推断出[String]是shoppingList中变量的正确类型。
5、访问和修改数组
我们可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
可以使用数组的只读属性count来获取数组中的数据项数量
var shoppingList = ["Eggs", "Milk"]
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 现在有七项了
我们也可以用下标来改变某个已有索引值对应的数据值:
shoppingList[0] = "Six eggs"
// 其中的第一项现在是 "Six eggs" 而不是 "Eggs"
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把"Chocolate Spread","Cheese",和"Butter"替换为"Bananas"和 "Apples":
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"
6、数组的遍历
我们可以使用for-in循环来遍历所有数组中的数据项:
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
// Apples
如果我们同时需要每个数据项的值和索引值,可以使用enumerated()方法来进行数组遍历。enumerated()返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
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
// Item 5: Apples
-
集合(Sets)
用来存储相同类型
并且没有确定顺序
的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次
时可以使用集合。
一个Set类型不能从数组字面量中被单独推断出来,因此Set类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个Set并且该数组字面量中的所有元素类型相同,那么你无须写出Set的具体类型。favoriteGenres的构造形式可以采用简化的方式代替:
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
由于数组字面量中的所有元素类型相同,Swift 可以推断出Set<String>作为favoriteGenres变量的正确类型。
-
字典(Dictionaries)
是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。
Swift 的字典使用Dictionary<Key, Value>定义,其中Key是字典中键的数据类型,Value是字典中对应于这些键所存储值的数据类型。
也可以用[Key: Value]这样简化的形式去创建一个字典类型。
1、创建一个空字典
我们可以像数组一样使用构造语法创建一个拥有确定类型的空字典:
var namesOfIntegers = [Int: String] ()
// namesOfIntegers 是一个空的 [Int: String] 字典
创建了一个[Int: String]类型的空字典来储存整数的英语命名。它的键是Int型,值是String型。
如果上下文已经提供了类型信息,我们可以使用空字典字面量来创建一个空字典,记作[:](中括号中放一个冒号):
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
用字典字面量创建字典
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//airports字典被声明为一种[String: String]类型,这意味着这个字典的键和值都是String类型。
和数组一样,我们在用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。 airports字典也可以用这种简短方式定义
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//这个语句中所有的键和值都各自拥有相同的数据类型,Swift 可以推断出Dictionary<String, String>是airports字典的正确类型。
2、访问和修改字典
我们可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。
和数组一样,我们可以通过字典的只读属性count来获取某个字典的数据项数量:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
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 字典现在有三个数据项
我们也可以使用下标语法来改变特定键对应的值:
airports["LHR"] = "London Heathrow"
// "LHR"对应的值 被改为 "London Heathrow
可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值的类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则将返回nil:
if let airportName = airports["DUB"] {
print("The name of the airport is (airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// 打印 "The name of the airport is Dublin"
我们还可以使用下标语法来通过给某个键的对应值赋值为nil来从字典里移除一个键值对:
airports["APL"] = "Apple Internation"
// "Apple Internation" 不是真的 APL 机场, 删除它
airports["APL"] = nil
// APL 现在被移除了
3、字典遍历
我们可以使用for-in循环来遍历某个字典中的键值对。每一个字典中的数据项都以(key, value)元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
for (airportCode, airportName) in airports {
print("(airportCode): (airportName)")
}
// YYZ: Toronto Pearson
// DUB: Dublin
// LHR: London Heathrow
通过访问keys或者values属性,我们也可以遍历字典的键或者值:
for airportCode in airports.keys {
print("Airport code: (airportCode)")
}
// Airport code: YYZ
// Airport code: DUB
// Airport code: LHR
for airportName in airports.values {
print("Airport name: (airportName)")
}
// Airport name: Toronto Pearson
// Airport name: Dublin
// Airport name: London Heathrow
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受Array实例的 API 的参数,可以直接使用keys或者values属性构造一个新数组:
let airportCodes = [String] (airports.keys)
// airportCodes 是 ["YYZ","DUB", "LHR"]
12、控制流
-
Switch
在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用break语句。
每一个 case 分支都必须包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 无效,这个分支下面没有语句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 这段代码会报编译错误
-
Early Exit
像if语句一样,guard的执行取决于一个表达式的布尔值。我们可以使用guard语句来要求条件必须为真时,以执行guard语句后的代码。不同于if语句,一个guard语句总是有一个else从句,如果条件不为真则执行else从句中的代码。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello (name)")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in (location).")
}
greet(["name": "John"])
// 输出 "Hello John!"
// 输出 "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// 输出 "Hello Jane!"
// 输出 "I hope the weather is nice in Cupertino."
相比于可以实现同样功能的if语句,按需使用guard语句会提升我们代码的可读性。它可以使你的代码连贯的被执行而不需要将它包在else块中,它可以使你在紧邻条件判断的地方,处理违规的情况。
-
Checking API Availability
我们在if或guard语句中使用可用性条件(availability condition)去有条件的执行一段代码,来在运行时判断调用的API是否可用。编译器使用从可用性条件语句中获取的信息去验证,在这个代码块中调用的 API 是否可用。
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
以上可用性条件指定,if语句的代码块仅仅在 iOS 10 或 macOS 10.12 及更高版本才运行。最后一个参数,*,是必须的,用于指定在所有其它平台中,如果版本号高于你的设备指定的最低版本,if语句的代码块将会运行。