控制流

if和guard

  • if let 解包后,if作用域外不可使用。
  • guard let 提前处理可选值为 nil 的情况,else 用来return异常情况,在解包后的值在后续代码继续使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func testA() -> String{
var c: String?
guard let cValue = c else {
return "c is nil"
}
return cValue
}

func testB() -> String{
var c: String?
if let cValue = c {
return "\(cValue) is not nil"
} else {
return "c is nil"
}
}

switch

  • 每个case无需break即可自动跳出
  • fallthrough:本case内容执行结束后,不检查下一个case(或default)条件直接执行
  • switch的case必须覆盖所有的值,如果没有被case覆盖或者值是无限的,必须使用default(若case已经覆盖所有值,则无需使用default)
  • 复合型case用逗号隔开
1
case “a”, “b”, “c” :
  • 支持let值绑定,where匹配
1
case let(x, y) where x == y:
  • 标签语句(可以给控制语句打标签),用于复杂嵌套的控制语句的逻辑跳转

函数

隐式返回值

  • 如果一个函数是一个单行表达式,则可以不写return。
1
2
3
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}

参数

标签

  • 写在参数名称前,以空格分隔,让函数在调用时更有表达力,更类似自然语言
1
2
3
4
5
6
7
8
9
func someFunction(argumentLabel parameterName: Int) {
// 在函数体内,parameterName 代表参数值
}
//eg:
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// 打印“Hello Bill! Glad you could visit from Cupertino.”
  • 使用_忽略参数标签
1
2
3
4
func addFunc(_ first: Int, second: Int) {
print(first+second)
}
addFunc(1, second: 2)

可变参数

  • 变量类型名后加****变成一个数组
1
2
3
4
5
6
7
8
9
10
11
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)
// 返回 3.0, 是这 5 个数的平均数。
arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。

输入输出参数

  • 参数类型前加inout标记,调用时在变量前加&
1
2
3
4
5
6
7
8
9
10
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印“someInt is now 107, and anotherInt is now 3”

函数类型

作为参数类型

1
2
3
4
5
6
7
8
9
//函数类型 (Int, Int) -> Int
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// 打印“Result: 8”

作为返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 现在指向 stepBackward() 函数。
print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

嵌套函数

  • 默认情况下,嵌套函数是对外界不可见的,但是可以被它们的外围函数(enclosing function)调用。一个外围函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用
1
2
3
4
5
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}

闭包

1
2
3
4
5
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)

闭包表达式语法

1
2
3
4
5
6
7
{ (parameters) -> return type in
statements
}
//backward对应的闭包表达式版本为
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})

根据上下文推断类型

  • 因为sorted(by:)方法的参数必须是(String, String) -> Bool类型的函数,所以可以省略闭包表达式的参数和返回值类型、箭头、参数周围的括号
1
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
  • 单表达式闭包的隐式返回:闭包函数体只有一个表达式时,可以省略return
1
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

参数名称缩写

  • 通过 $0 $1 $2来顺序调用闭包的参数,可以省略参数定义。闭包接受的参数的数量取决于所使用的缩写参数的最大编号。in关键字也同样可以被省略
1
reversedNames = names.sorted(by: { $0 > $1 } )

运算符方法

  • String 类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个 String类型的参数并返回 Bool类型的值。与sorted(by:) 方法的参数需要的函数类型相符合。故可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现
1
reversedNames = names.sorted(by: >)

尾随闭包

定义

  • 尾随闭包:写在函数圆括号之后的闭包表达式
  • 适用于:一个较长的闭包表达式作为最后一个参数时
  • 一个函数可以有多个尾随闭包,第一个不写参数标签,其余的尾随闭包要写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}

// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
// 闭包主体部分
})

// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}

reversedNames = names.sorted() { $0 > $1 }

// 如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉
reversedNames = names.sorted { $0 > $1 }

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

let strings = numbers.map { (number) -> String in //number类型无需指定,会自动推断
var number = number //闭包或函数的参数总是常量,故需赋值给一个局部变量
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
// strings = ["OneSix", "FiveEight", "FiveOneZero"]

//多个尾随闭包
func loadPicture(from server: Server, completion:(Picture) -> Void,
onFailure: () -> Void) {
if let picture = download("photo.jpg", from: server){
completion(picture)
}else{
onFailure()
}
}

loadPicture(from: someServer){ picture in
someView.currentPicture = picture
} onFailure: {
print("Couldn't download the next picture.")
}

逃逸闭包

  • 闭包作为参数传到一个函数中,但这个闭包在函数返回后才执行,称为逃逸闭包。
  • 函数参数名之前标注 @escaping 表示允许这个闭包逃逸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var completionHandlers: [() -> Void] = []

func someFuncWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}

func someFuncWithNoneEscapingClosure(closure: () -> Void) {
closure()
}

class SomeClass {
var x = 10
func doSomething() {
someFuncWithEscapingClosure { self.x = 100 } //逃逸闭包必须显式引用self
someFuncWithNoneEscapingClosure { x = 200 } //非逃逸闭包可以隐式引用self
}
}

let instance = SomeClass()
instance.doSomething()
print(instance.x) //200
completionHandlers.first?()
print(instance.x) //100

自动闭包

  • 自动创建的闭包,用于包装传递给函数作为参数的表达式
  • 自动闭包不接受参数,可以延迟求值(执行表达式),被调用时会返回包装在其中的表达式的值
1
2
3
4
5
6
7
//延迟求值
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count) // 5
let customerProvider = { customersInLine.remove(at: 0) } // 此时表达式不执行
print(customersInLine.count) // 5
print("Now serving \(customerProvider())!") //Now serving Chris! 此时表达式才执行
print(customersInLine.count) // 4
  • 参数标记@autoclosure无需传入闭包,传入的表达式会自动转化为一个闭包
1
2
3
4
5
6
7
8
9
10
11
//闭包作为普通参数 延迟求值
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } ) //参数为闭包

//闭包被标记为自动闭包
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0)) //参数为String类型
  • 让自动闭包可以”逃逸“,应同时使用@autoclosure@escaping 属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// customersInLine i= ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))

print("Collected \(customerProviders.count) closures.")
// 打印“Collected 2 closures.”
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// 打印“Now serving Barry!”
// 打印“Now serving Daniella!”

枚举

语法

  • 大写字母开头
  • 可以只有case没有值(不会被赋予默认值)
  • 多个成员值可以在同一行,用隔开
1
2
3
4
enum SomeEnum {
case a
case b, c
}

枚举成员的遍历

  • 遵循 CaseIterable协议,Swift 会生成一个 allCases属性,表示所有枚举的集合
1
2
3
4
5
6
enum Beverage: CaseIterable {
case coffee, tea, juice
}
for beverage in Beverage.allCases {
print(beverage)
}

关联值

  • 把其他类型的值和成员值一起存储,这额外的信息叫关联值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Barcode {
case upc(Int, Int, Int, Int) //条形码
case qrCode(String) //二维码
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// 打印“QR code: ABCDEFGHIJKLMNOP.”

原始值

定义

  • 提供默认值,类型必须相同
1
2
3
4
5
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}

隐式原始值

  • 无需显示赋值,Swift会自动赋值
  • 整数作为原始值:默认0开始递增+1。 字符串作为原始值:枚举成员名称
  • 使用rawValue访问原始值
1
2
3
4
5
6
7
8
9
10
11
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
enum CompassPoint: String {
case north, south, east, west
}
let earthsOrder = Planet.earth.rawValue
// earthsOrder 值为 3

let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection 值为 "west"

递归枚举

  • 递归枚举是一种枚举类型,一个或多个枚举成员使用该枚举类型的实例作为关联值。
  • indirect关键字表示可递归,可标记指定枚举成员或枚举类型开头标记所有成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product)) //18

类和结构体

  • 结构体是值类型,赋值给常量或变量时是值拷贝
  • 类是引用类型,赋值给常量或变量时是引用

恒等运算符

用于判断两个常量或者变量是否引用同一个类实例

  • 相同(===
  • 不相同(!==

属性

存储属性

常量结构体实例的存储属性

  • 结构体属于值类型,实例被声明为常量时,所有属性也就变成了常量,不可修改
  • 类是引用类型,即使类实例赋值给常量,实例的可变属性仍可被修改
1
2
3
4
5
6
7
8
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 该区间表示整数 0,1,2,3
rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个可变属性,但这里还是会报错

延时加载存储属性

  • 属性前使用lazy标示,被调用到时才会加载
  • lazy属性还没初始化时就被多个线程访问,无法保证只会被初始化一次

存储属性和实例变量

  • OC有与属性对应的实例变量(即_开头的成员变量)
  • Swift没有实例变量,统一用属性

计算属性

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
// initialSquareCenter 位于(5.0, 5.0)
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 打印“square.origin is now at (10.0, 10.0)”

简化Setter声明

  • 如果setter没有定义表示新值的参数名,则可以使用默认名称newValue
1
2
3
4
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}

简化Getter声明

  • 如果getter是单一表达式,可使用隐式返回,省略return
1
2
3
4
get {
Point(x: origin.x + (size.width / 2),
y: origin.y + (size.height / 2))
}

只读计算属性

  • 只有 getter 没有 setter 的计算属性叫只读计算属性
  • 可以去掉 get关键字和花括号
1
2
3
var volume: Double {
return width * height * depth
}

属性观察器

  • willSet在新的值被设置之前调用,不指定参数时默认为newValue
  • didSet在新的值被设置之后调用,不指定参数时默认为oldValue
  • willSet用于观察,可以验证新值、记录日志、更新其他属性,但不能修改即将设置的新值
  • didSet用于响应可以修改已经设置的新值(不会重新执行willSet和didSet)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("将 totalSteps 的值设置为 \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("增加了 \(totalSteps - oldValue) 步")
} else {
totalSteps = oldValue //属于异常情况,让新值作废
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// 将 totalSteps 的值设置为 200
// 增加了 200 步
stepCounter.totalSteps = 100
print("total=\(stepCounter.totalSteps)")
// total=200

属性包装器

  • 用于给每个属性添加相同的代码逻辑
  • @propertyWrapper标识,创建一个定义wrappedValue属性的结构体、枚举或类
  • 可用于存储属性、局部变量上,不能用于全局变量、全局常量、局部常量、计算型属性
  • 可以在局部存储型变量上使用属性包装器,但不能在全局常量、全局变量或者计算型变量上使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 定义
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
// 使用
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height) // 0

rectangle.height = 10
print(rectangle.height) // 10

rectangle.height = 24
print(rectangle.height) // 12

设置属性包装器的初始值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int

var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}

init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
// 使用 init() 构造器来设置包装器
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
// 使用 init(wrappedValue:) 构造器来设置包装器
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
// 使用 init(wrappedValue:maximum:) 构造器 (支持以下两种形式)
struct NarrowRectangle {
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(maximum: 4) var width: Int = 3
}

从属性包装器中呈现一个值

  • 按如下方式定义,属性前加$访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@propertyWrapper
struct SmallNumber {
private var number: Int
private(set) var projectedValue: Bool

var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}

init() {
self.number = 0
self.projectedValue = false
}
}
struct SomeStructure {
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// 打印 "false"

someStructure.someNumber = 55
print(someStructure.$someNumber)
// 打印 "true"

全局变量和局部变量

  • 全局的常量或变量都是延迟计算的,无需加lazy修饰

类型属性

  • 存储型类型属性必须指定默认值(因为类型本身没有构造器,无法在初始化过程中赋值)
  • 存储型类属性是延迟初始化的,无需使用lazy,且线程安全只会初始化一次(前提是设置了默认值)

语法

  • 使用static定义类型属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
print(SomeStructure.storedTypeProperty)
// 打印“Some value.”
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// 打印“Another value.”
print(SomeEnumeration.computedTypeProperty)
// 打印“6”
print(SomeClass.computedTypeProperty)
// 打印“27”

方法

  • 类、结构体、枚举都可以定义实例方法和类型方法

实例方法

  • 实例方法能够访问它所属类型的所有的其他实例方法和属性

self属性

  • 使用当前实例的属性或方法时,self可省略
  • 实例方法参数名称与属性名称相同时,参数名称享有优先权,需加上self用于区分,否则Swift会认为两个都指的参数名称
1
2
3
4
5
6
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}

在实例方法中修改值类型

  • 结构体和枚举是值类型,默认情况下,值类型的属性不能在它的实例方法中被修改
  • 想修改,需在方法前加mutating关键字
  • 不能在结构体类型的常量上调用可变方法
1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
// 打印“The point is now at (3.0, 4.0)”
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// 这里将会报告一个错误

在可变方法中给 self 赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
enum TriStateSwitch {
case off, low, high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight 现在等于 .high
ovenLight.next()
// ovenLight 现在等于 .off

类型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1

static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}

static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}

@discardableResult // 忽略返回值
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}

下标

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
subscript(index: Int) -> Int {
get {
// 返回一个适当的 Int 类型的值
}
set(newValue) {
// 执行适当的赋值操作
}
}

struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// 打印“six times three is 18”

下标选项

  • 支持多个参数(但不能用inout参数)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    subscript(row: Int, column: Int) -> Double {
    get {
    assert(indexIsValid(row: row, column: column), "Index out of range")
    return grid[(row * columns) + column]
    }
    set {
    assert(indexIsValid(row: row, column: column), "Index out of range")
    grid[(row * columns) + column] = newValue
    }
    }

继承

  • Swift 中的类并不是从一个通用的基类继承而来的。如果你不为自己定义的类指定一个超类的话,这个类就会自动成为基类。

重写

  • 使用override关键字可重写实例方法,类方法,实例属性,类属性,或下标

访问超类的方法,属性及下标

  • 在方法 someMethod() 的重写实现中,可以通过 super.someMethod() 来调用超类版本的 someMethod() 方法。
  • 在属性 someProperty 的 getter 或 setter 的重写实现中,可以通过 super.someProperty 来访问超类版本的 someProperty 属性。
  • 在下标的重写实现中,可以通过 super[someIndex] 来访问超类版本中的相同下标。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// 什么也不做——因为车辆不一定会有噪音
}
}
class Car: Vehicle {
var gear = 1
override var description: String {//重写属性
return super.description + " in gear \(gear)"
}
}
class AutomaticCar: Car {
override var currentSpeed: Double {//重写属性观察器
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}

防止重写

  • 试图对final标记的方法、属性或下标进行重写的代码会编译时报错
  • 类扩展中也可以用final
  • class前使用final修饰(final class)使整个类无法被继承

构造过程

⭐️ Swift的构造器没有返回值

存储属性的初始赋值

  • 存储属性必须赋初始值
  • 可以定义时设置,也可以构造器中设置

类的继承和构造过程

指定构造器和便利构造器

  • 指定构造器必须调用其直接父类的的指定构造器
  • 便利构造器必须调用类中定义的其它构造器
  • 便利构造器最后必须调用指定构造器

指定构造器必须总是向上代理

便利构造器必须总是横向代理

两段式构造

阶段一

  • 类的某个指定构造器或便利构造器被调用。
  • 完成类的新实例内存的分配,但此时内存还没有被初始化。
  • 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
  • 指定构造器切换到父类的构造器,对其存储属性完成相同的任务。
  • 这个过程沿着类的继承链一直往上执行,直到到达继承链的最顶部。
  • 当到达了继承链最顶部,而且继承链的最后一个类已确保所有的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。

阶段二

  • 从继承链顶部往下,继承链中每个类的指定构造器都有机会进一步自定义实例。构造器此时可以访问 self、修改它的属性并调用实例方法等等。
  • 最终,继承链中任意的便利构造器有机会自定义实例和使用 self

构造器重写

  • 与父类指定构造器或便利构造器相同时,要加上override进行重写(参数列表顺序不同不算相同)

可失败构造器

  • init 关键字后面添加问号(init?),在不能正确构造时,返回nil表示失败
  • 可以用非可失败构造器重写可失败构造器,但反过来却不行
  • init!该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象,构造失败时触发断言

必要构造器

  • 在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器
  • 重写父类必要构造器时,子类也要加required,无需加override
  • 虽然是必须实现,但如父类已满足需求,子类实现内容可以为空
1
2
3
4
5
class SomeSubclass: SomeClass {
required init() {
// 构造器的实现代码(父类满足时可留空)
}
}

通过闭包或函数设置属性的默认值

  • 闭包结尾的花括号后面接了一对空的小括号,这用来告诉 Swift 立即执行此闭包,并将返回值赋值给属性。(如果没有括号,代表这个属性是一个闭包)
  • 闭包执行时,实例其他部分还没初始化,所以不能访问其他属性或self
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard: [Bool] = []
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}

析构过程

1
2
3
deinit {
// 执行析构过程
}
  • 不能主动调用析构器,子类继承了父类的析构器
  • 子类析构器实现的最后,父类的析构器会被自动调用

可选链

可选链式调用的返回值是相同类型的可选值

1
2
3
4
5
6
7
var str: String?
str = "aaaa"
if let beginsWithA = str?.hasPrefix("a") {
print("yes")
} else {
print("no")
}
  • str?.hasPrefix(“a”)返回值是 Bool? 而if后面需要Bool类型,所以需要let绑定或与nil比较
  • 可选链式调用来设置一个值,并且这个赋值操作会返回一个 Void? 类型的值
  • 如果赋值失败,则返回nil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
var residence: Residence?
}

class Residence {
var numberOfRooms = 1
}

let john = Person()
//如果这里加一句 john.residence = Residence() 则输出 a
if (john.residence?.numberOfRooms = 2) != nil {
print("a")
} else {
print("b")
}
// 输出 b

Dictionary中通过下标访问

1
2
3
4
5
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// "Dave" 数组现在是 [91, 82, 84],"Bev" 数组现在是 [80, 94, 81]

错误处理

如果一个函数或方法可能抛出错误,则调用时必须使用try、try?、try!

将错误转换成可选值

  • xy 有着相同的数值和等价的含义
1
2
3
4
5
6
7
8
9
10
11
12
func someThrowingFunction() throws -> Int {
// ...
}

let x = try? someThrowingFunction()

let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}

defer用于函数退出或作用域结束时清理工作

  • 函数正常结束或提前结束都会执行defer内容
1
2
3
4
5
6
7
8
9
10
11
12
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// 处理文件。
}
// close(file) 会在这里被调用,即作用域的最后。
}
}

并发

  • async标记异步函数
  • 调用异步函数前加await代表这个方法被挂起
1
2
3
4
5
6
7
8
9
10
func listPhotos(inGallery name: String) async -> [String] {
let result = // 省略一些异步网络请求代码
return result
}

let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)

类型转换

检查类型 is

1
2
3
4
5
6
7
8
9
10
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")

向下转型 as?或as!

1
2
3
4
5
6
7
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}

AnyAnyObject 的类型转换

  • Any 可以表示任何类型,包括函数类型。
  • AnyObject 可以表示任何类类型的实例

扩展

  • 添加计算型实例属性和计算型类属性
1
2
3
4
5
6
7
8
9
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")

不能添加存储属性,或向现有的属性添加属性观察者。

  • 使已经存在的类型遵循(conform)一个协议
1
2
3
extension SomeType: SomeProtocol, AnotherProtocol {
// 协议所需要的实现写在这里
}
  • 定义实例方法和类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 新的实例方法
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}

// 可变实例方法
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt 现在是 9
  • 提供新的构造器
1
2
3
4
5
6
7
extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
  • 定义下标
  • 定义和使用新的嵌套类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}

协议

协议作为类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
//任何遵循了 RandomNumberGenerator 协议的类型的实例都可以赋值给 generator

var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
// Random dice roll is 3
// Random dice roll is 5
// Random dice roll is 4
// Random dice roll is 5
// Random dice roll is 4

委托

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protocol MyDelegate {
func myplay()
}

class MyClass {
var delegate: MyDelegate?
func doSomething() {
print("let's do something")
delegate?.myplay()
}
}

class SomeClass: MyDelegate {
func myplay() {
print("SomeClass is playing")
}
}
let a = SomeClass()
let b = MyClass()
b.delegate = a
b.doSomething()
//let's do something
//SomeClass is playing

有条件地遵循协议

  • 下面的扩展让 Array 类型只要在存储遵循 MyDelegate 协议的元素时就遵循 MyDelegate 协议
1
2
3
4
5
extension Array: MyDelegate where Element: MyDelegate {
func play() {
//自定义实现
}
}

使用合成实现来采纳协议

Swift 可以自动提供一些简单场景下遵循 EquatableHashableComparable 协议的实现,在使用这些合成实现之后,无需再编写重复的代码来实现这些协议所要求的方法

Swift 为以下几种自定义类型提供了 Equatable 协议的合成实现:(提供了!===操作符的默认实现)

  • 遵循 Equatable 协议且只有存储属性的结构体。
  • 遵循 Equatable 协议且只有关联类型的枚举
  • 没有任何关联类型的枚举
1
2
3
4
// 无需实现 == 和 !=
struct Vector3D: Equatable {
var x = 0.0, y = 0.0, z = 0.0
}

Swift 为以下几种自定义类型提供了 Hashable 协议的合成实现(提供了hash(into:)的默认实现):

  • 遵循 Hashable 协议且只有存储属性的结构体。
  • 遵循 Hashable 协议且只有关联类型的枚举
  • 没有任何关联类型的枚举

Swift 为没有原始值的枚举类型提供了 Comparable 协议的合成实现。如果枚举类型包含关联类型,那这些关联类型也必须同时遵循 Comparable 协议。(提供了>, <, >=, <=的默认实现)

1
2
3
4
5
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
}

协议的继承

协议可以继承自一个或多个协议(逗号隔开)

1
2
3
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 这里是协议的定义部分
}

类专属的协议

  • 通过添加AnyObject到协议继承列表,限制协议只能被类类型遵循(而不能是结构体类型或者枚举类型)。
1
2
3
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// 这里是类专属协议的定义部分
}

协议合成

要求一个类型同时遵循多个协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// 打印 “Happy birthday Malcolm - you're 21!”

检查协议一致性

  • is 用来检查实例是否遵循某个协议,若遵循则返回 true,否则返回 false
  • as? 返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回 nil
  • as! 将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。

可选的协议要求

  • 可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上 @objc 属性。标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}

class ThreeSource: NSObject, CounterDataSource {
let fixedIncrement = 3
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
// 3
// 6
// 9
// 12

泛型

泛型函数

1
2
3
4
5
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}

类型约束

  • T必须是SomeClass子类,U必须符合SomeProtocol协议
1
2
3
4
5
6
7
8
9
10
11
12
13
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}

//T类型必须能被比较(Equatable要求实现==和!=)
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}