1.Swift 基础类型
Swift
包含了 C
和 Objective-C
上所有基础数据类型,Int
表示整型值; Double
和 Float
表示浮点型值; Bool
是布尔型值;String
是文本型数据。 Swift
还提供了三个基本的集合类型,Array
,Set
和Dictionary
。Swift
还增加了Objective-C
中没有的高阶数据类型比如元组(Tuple
)。Swift
还增加了可选(Optional
)类型,用于处理值缺失的情况。可选类型比 Objective-C 中的 nil 指针更加安全也更具表现力。
2.常量和变量
常量和变量必须在使用前声明,用let
来声明常量,用var
来声明变量。
let a = 10
var b = 11
var x = 0.0, y = 0.0, z = 0.0
当你声明常量或者变量的时候可以加上类型标注
var str: String
给str
变量添加了类型标注
,表示这个变量可以存储 String
类型的值:
可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型标注:
var red, green, blue: Double
3.类型安全
和类型推断
Swift
是一个类型安全(ype safe
)的语言.类型安全的语言可以让你清楚地知道代码要处理的值的类型。 Swift
是类型安全的,所以它会在编译
你的代码时进行类型检查
.由于类型推断
,大部分工作时候声明变量或常量的时候并不需要你自己来完成。
当推断浮点数的类型时,Swift
总是会选择Double
而不是Float
。
4.数值型字面量
一个十进制数,没有前缀
一个二进制数,前缀是0b
一个八进制数,前缀是0o
oc或者c是 0
一个十六进制数,前缀是0x
数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
整数转换
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
let integerPi = Int(pi)
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 3.895
会变成 3
,-10.9
会变成 -10
。
如果在需要使用Bool
类型的地方使用了非布尔值,Swift
的类型安全机制会报错。
let i = 1
if i {
// 这个例子不会通过编译,会报错
}
let i = 1
if i == 1 {
// 这个例子会编译成功
}
*注意:Int
与Int8
的区别
5.元组
元组(tuples
)把多个值组合成一个复合值。元组内的值可以是任意类型
,并不要求是相同类型。
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")
可以将一个元组的内容分解(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"
*注意:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
6.nil
C
和 Objective-C
中并没有可选类型这个概念。最接近的是Objective-C
中的一个特性,一个方法要不返回一个对象要不返回nil
,nil
表示“缺少一个合法的对象”。这只对对象起作用——对于结构体,基本的 C 类型
或者枚举类型
不起作用。对于这些类型,Objective-C
方法一般会返回一个特殊值(比如NSNotFound
)来暗示值缺失。Swift
的可选类型
可以让你暗示任意类型
的值缺失
,并不需要一个特殊值。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil
:
var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
7.可选类型
使用可选类型(optionals
)来处理值可能缺失的情况。
一个可选的 Int
被写作Int?
而不是Int
。问号暗示包含的值是可选类型,也就是说可能包含 Int
值也可能不包含值。
使用 !
来获取一个不存在的可选值
会导致运行时错误。使用!
来强制解析
值之前,一定要确定可选包含一个非nil
的值。
8.可选绑定
使用可选绑定(optional binding
)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。
可选绑定可以用在if
和 while
语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
if let constantName = someOptional {
statements
}
理解:
“如果 Int(possibleNumber)
返回的可选 Int
包含一个值,创建一个叫做 actualNumber
的新常量并将可选包含的值赋给它。”
如果转换成功,actualNumber
常量可以在if
语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 !
后缀来获取它的值。在这个例子中,actualNumber
只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在if语句的第一个分支中操作 actualNumber
的值,你可以改成 if var actualNumber
,这样可选类型包含的值就会被赋给一个变量而非常量。
9.隐式解析可选类型
可选类型暗示了常量或者变量可以“没有值”。可选可以通过 if
语句来判断是否有值,如果有值的话可以通过可选绑定
来解析值。
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型
(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 // 不需要感叹号
可以把隐式解析可选类型
当做一个可以自动解析
的可选类型
。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
10.错误处理
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}
一个函数可以通过在声明中添加throws
关键词来抛出错误消息。当你的函数能抛出错误消息时, 你应该在表达式中前置try
关键词。
do {
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
11.断言
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个断言(assertion)
来结束代码运行并通过调试来找到值缺失的原因。
使用断言进行调试
断言会在运行时判断一个逻辑条件是否为 true
。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为 true
,代码运行会继续进行;如果条件判断为 false
,代码执行结束,你的应用被终止
。
你可以使用全局assert(_:_:file:line:)
函数来写一个断言。向这个函数传入一个结果为 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
,断言被触发,终止应用。
*注意:当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。断言的适用情景:
整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。
需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
一个可选值现在是 nil,但是后面的代码运行需要一个非 nil 值。
12.基本运算符
赋值运算符
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。
if x = y {
// 此句错误, 因为 x = y 并不返回任何值
}
加法运算符也可用于 String 的拼接:
"hello, " + "world" // 等于 "hello, world"
空合运算符(Nil Coalescing Operator)
空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。.
区间运算符(Range Operators)
闭区间运算符(a...b)
定义一个包含从 a
到 b
(包括 a 和 b)的所有值的区间。
半开区间运算符:半开区间(a..定义一个从
a
到 b
但不包括 b
的区间。
13.字符串
初始化空字符串 (Initializing an Empty String)
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
// 两个字符串均为空并等价。
空字符串的判断: .isEmpty
if emptyString.isEmpty {
print("Nothing to see here")
}
// 打印输出:"Nothing to see here"
字符串是值类型(Strings Are Value Types)
Swift
的String
类型是值类型。
如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作
在Swift
中,所有的基本类型:整数(Integer
)、浮点数(floating-point
)、布尔值(Boolean
)、字符串(string
)、数组(array
)和字典(dictionary
),都是值类型,并且在底层都是以结构体的形式所实现。
Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
使用字符(Working with Characters)
通过标明一个Character类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
let exclamationMark: Character = "!"
字符数组:
let catCharacters: [Character] = ["C", "a", "t", "!", ""]
let catString = String(catCharacters)
print(catString)
// 打印输出:"Cat!"
连接字符串和字符
字符串可以通过加法运算符(+
)相加在一起(或称“连接”)创建一个新的字符串:
您也可以通过加法赋值运算符 (+=
) 将一个字符串添加到一个已经存在字符串变量上:
可以用append()
方法将一个字符附加到一个字符串变量的尾部
字符串插值 (String Interpolation)
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
Unicode
Unicode 是一个国际标准,用于文本的编码和表示。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
Swift
的String
和Characte
r类型是完全兼容 Unicode
标准的。
Unicode 标量(Unicode Scalars)
可扩展的字形群集(Extended Grapheme Clusters)
使用 characters
属性的 indices
属性会创建一个包含全部索引的范围(Range
),用来在一个字符串中访问单个字符。
插入和删除 (Inserting and Removing)
字符串/字符可以用等于操作符(==)和不等于操作符(!=)
前缀/后缀相等 (Prefix and Suffix Equality)
14.集合类型 (Collection Types)
Swift
的Arrays
、Sets
和Dictionaries
类型被实现为泛型集合
。
Swift
语言提供Arrays
、Sets
和Dictionaries
三种基本的集合类型用来存储集合数据
。数组(Arrays
)是有序数据的集。集合(Sets
)是无序无重复数据的集。字典(Dictionaries
)是无序的键值对的集。
在我们不需要改变集合的时候创建不可变集合是很好的实践。如此 Swift 编译器可以优化我们创建的集合。
1)数组:
创建一个空数组
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
可以使用append(_:)
方法在数组后面添加新的数据项:
使用加法赋值运算符(+=)
也可以直接在数组后面添加一个或多个拥有相同类型的数据项
shoppingList += ["Baking Powder"]
// shoppingList 现在有四项了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 现在有七项了
调用数组的insert(_:atIndex:)
方法来在某个具体索引值之前添加数据项:
shoppingList.insert("Maple Syrup", atIndex: 0)
// shoppingList 现在有7项
// "Maple Syrup" 现在是这个列表中的第一项
类似的我们可以使用removeAtIndex(_:)方法来移除数组中的某一项。
for item in shoppingList {
print(item)
}
for (index, value) in shoppingList.enumerate() {
print("Item \(String(index + 1)): \(value)")
}
Swift
的所有基本类型(比如String
,Int
,Double
和Bool
)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在枚举有讲述)默认也是可哈希化的。
2)集合
3)字典
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
Swift
的Dictionary
类型被桥接到Foundation
的NSDictionary
类。
一个字典的Key
类型必须遵循Hashable
协议,就像Set
的值类型。
var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
一个键值对是一个key和一个value的结合体
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
`airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
作为另一种下标方法,字典的updateValue(_:forKey:)
方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,updateValue(_:forKey:)
方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和上面的下标方法不同的,updateValue(_:forKey:)
这个方法返回更新值之前的原值。这样使得我们可以检查更新是否成功。
可以使用下标语法来通过给某个键的对应值赋值为nil
来从字典里移除一个键值对
:
airports["APL"] = "Apple Internation"
// "Apple Internation" 不是真的 APL 机场, 删除它
airports["APL"] = nil
// APL 现在被移除了
removeValueForKey(_:)
方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回nil
:
if let removedValue = airports.removeValueForKey("DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// prints "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属性,我们也可以遍历字典的键或者值:
airports.keys
和 airports.values
构造函数:
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
Swift
的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的keys
或values
属性使用sort()
方法。
15.函数
函数
是一段完成特定任务的独立代码片段
。你可以通过给函数命名来标识某个函数的功能,这个名字可以被用来在需要的时候"调用"这个函数来完成它的任务。
输入输出参数(In-Out Parameters)
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。
定义一个输入输出参数时,在参数定义前加 inout 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看输入输出参数一节。
你只能传递变量给输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 & 符,表示这个值可以被函数修改。
可变参数 (Variadic Parameters)
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers