控制流

2023/1/26

# 类型整理

# 所有基础类型

  • Int
  • Float: 32位浮点数。精度要求不高的话可以使用此类型
  • Double: 64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型
    • 当推断浮点数的类型时,Swift 总是会选择 Double 而不是 Float。
  • Bool
  • tuples: 元组,元组相比数组,并不会要求所有值是一个类型
    • let http404Error = (404, "Not Found")
  • optionals: 可选类型
  • nil: 不是指针,它是一个确定的值,用来表示值缺失
  • tuple:元组,定义一组数据,定义时就确定了每个位置的类型

typealias AudioSample = UInt16 定义类型别名

let a = 3.14
print(type(of: a)); // Double
1
2

# tuple

  • 元组,可以用来做为函数返回值
var stu = ("hdy", 18, "170", ["你不知道的JS"])

stu.3.append("JS高级编程")

print(stu); // ("hdy", 18, "170", ["你不知道的JS", "JS高级编程"])
1
2
3
4
5

# 解构可选类型的方法

  • 可选类型:不确定是否有值
    let a = "0123"
    
    let b = Int(a)
    print(b); // Optional(123)
    
    1
    2
    3
    4
  • 默认值解构
    let a = "123"
    
    let b = Int(a) ?? 0
    print(b); // 123
    
    1
    2
    3
    4
  • 强制解构,尽量不用
    let a = "123"
    
    let b = Int(a)!
    print(b); // 123
    
    1
    2
    3
    4
  • if解构(如果确定是optional类型,声明可以重名)
    let a = "123"
    
    let b = Int(a)
    
    if let b = b {
        print(b)
    }
    
    1
    2
    3
    4
    5
    6
    7
  • if多条件并列判断
    let a = "123"
    
    let b = Int(a)
    
    // 不能使用 || 运算符
    if let b = b, a == "123"{
        print(b)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

# 强制解包

  • 尽量不要使用强制解包
  • 强制解包都可以被if let...语句替代
  • 当可选值为空时可选链式调用只会调用失败,然而强制解包将会触发运行时错误

正常情况(强制解包)

class People {
    var name: String?
}

let people = People()
people.name = "小黄"
print(people.name!); // 小黄
1
2
3
4
5
6
7

运行时错误

class People {
    var name: String?
}

let people = People()

print(people.name!); // 运行时错误
1
2
3
4
5
6
7

# 断言调试

  • assert传入正确值,假如遇到false值会中断执行并报相应的错误
    let a = "123"
    
    assert(a == "123", "a 等于123,不理他")
    
    let b = Int(a)
    
    assert(a == "234", "a 不等于234"); // 断言停止执行并报错
    
    print("到不了我")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 先决条件precondition:当一个条件可能为假,但是继续执行代码要求条件必须为真的时候,需要使用先决条件
    let a = "123"
    
    precondition(a == "123", "a 不等于123")
    
    let b = Int(a)
    
    precondition(a == "234", "a 不等于234"); // 断言停止执行并报错
    
    print("到不了我")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 特殊写法(写进Number里面)

  • 进制整理
let decimalInteger = 17
let binaryInteger = 0b10001       // 二进制的17
let octalInteger = 0o21           // 八进制的17
let hexadecimalInteger = 0x11     // 十六进制的17
1
2
3
4
  • 简洁写法: 整数和浮点数都可以添加额外的零并且包含下划线
let a = 1_000_000
print(a); // 1000000
1
2

# 数字类型转换

let a = 1_000_000
print(Double(a)); // 1000000.0

let b = 3.14
print(Int(b)); // 3
1
2
3
4
5

# 数组和字典

  • 初始化空
    shoppingList = []
    occupations = [:]
    
    1
    2
  • 数组
    var students: [String] = ["小黄", "效力", "小周", "小黑"]
    students[1] = "小李"
    print(students)
    
    1
    2
    3
  • 字典
    var teach: [String: Float] = [
        "teacher": "周老师",
        "学生": "小黄",
    ]
    teach["课程"] = "swift"
    print(teach["学生"] ?? "")
    print(teach["学生"]!)
    print(teach)
    
    1
    2
    3
    4
    5
    6
    7
    8

# 控制流

# for in

  • for in
    let codes = [1, 2, 3, 4, 5]
    var totle = 0
    
    for code in codes {
        totle += code
    }
    
    print(totle)
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 区间运算符(...)
    var score = 3;
    
    // 打印 0 1 2 3
    for i in 0...score {
        print(i)
    }
    
    // 打印 0 1 2 
    for i in 0..<score {
        print(i)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • stride 跳过条件
    let minute = 60;
    let jump = 5
    
    for seconds in stride(from: 0, to: minute, by: jump) {
        print(seconds) ; // 0 5 10 15 20 25 30 35 40 45 50 55
    }
    
    1
    2
    3
    4
    5
    6

# 单侧区间

let names = [1, 2, 3, 4, 5, 6, 7]
for name in names[2...] {
    print(name) // 34567
}

for name in names[...2] {
    print(name) // 123
}

for name in names[..<2] {
    print(name) // 12
}
1
2
3
4
5
6
7
8
9
10
11
12

# if 赋值:去optional

let name: String? = "黄德宇";
print(name); // Optional("黄德宇")

if let a = name {
    print("你好\(a)") // 你好黄德宇
}
1
2
3
4
5
6

# guard

  • 和if语句判断相反,且必须有else语句,主要用来保护后面代码正确执行
var name: String?

func hello(_ name: String?) {
    guard let name else {
        print("你好啊,无名氏")
        return;
    }
    print("你好呀,\(name)")
}

hello(name)
1
2
3
4
5
6
7
8
9
10
11

# switch

  • 直接判断
  • 多值判断
  • 语句判断

运行 switch 中匹配到的 case 语句之后,程序会退出 switch 语句,并不会继续向下运行,所以不需要在每个子句结尾写 break。 同时如果需要贯穿要加关键字fallthrough

let score = 97

switch score {
case let s where s < 0 || s > 100:
    print("错误分数")
case 100:
    print("满分")
case 60,61,62:
    print("刚刚及格")
case 95...99:
    print("差点满分")
case let s where s < 60:
    print("不及格")
default:
    print("考得不错")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 元组情况
    • 可以使用_来匹配所有值
    • 用let分别取作用域别名
let score = ("小黄", 20)

switch score {
case (_, 60):
    print("刚刚及格")
case (let name, 20):
    print("\(name)考的真差")// 小黄考的真差
default:
    print("考得不错")
}
1
2
3
4
5
6
7
8
9
10

# while / repeat...while

  • while 直接判断
    var score = 8;
    
    while score < 10 {
        print(score); // 8 9
        score += 1
    }
    
    1
    2
    3
    4
    5
    6
  • repeat...while 执行一次再判断
var score = 10;

repeat {
    print(score); // 10
    score += 1
} while score < 10
1
2
3
4
5
6

# 函数与闭包

# 函数基础写法

  • 函数只有单行表达式表达式的值匹配函数返回值类型会直接被作为返回值
func sum (a: Int, b: Int)-> Int {
    a + b
}

let a = 1
let b = 2

let c = sum(a: a, b: b)
print (c) // 3
1
2
3
4
5
6
7
8
9

# 可变参数长度

  • 接受不固定长度参数,形参用...转化为数组
func sum(_ nums: Double...) -> Double {
    nums.reduce(0, {$0 + $1})
}

print(sum(1, 2, 3, 4)); // 10
1
2
3
4
5

# 函数标签

  • 标签,标签调用顺序不可以乱序
    func sum (p1 a: Int, p2 b: Int)-> Int {
        return a + b
    }
    
    let a = 1
    let b = 2
    
    // 使用标签名称
    let c = sum(p1: a, p2: b)
    print (c) // 3
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • 不需要标签
    func sum (_ a: Int, _ b: Int)-> Int {
        return a + b
    }
    
    let a = 1
    let b = 2
    
    // 都不需要使用标签
    let c = sum(a, b)
    print (c) // 3
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • 元祖参数
func compire (scores: [Int])-> (min: Int, max: Int) {
    var min = scores[0]
    var max = scores[0]
    for i in scores {
        if (i < min) {
            min = i
        } else if (i > max) {
            max = i
        }
    }
    return (min, max)
}

let scores = [1, 3, 2, 100, -19]

let c = compire(scores: scores)
print (c) // (min: -19, max: 100)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 返回函数类型
// 函数返回类型声明参数不能有参数名
func getCompireFn() -> (([Int]) -> (min: Int, max: Int)) {
    func compire (scores: [Int])-> (min: Int, max: Int) {
        var min = scores[0]
        var max = scores[0]
        for i in scores {
            if (i < min) {
                min = i
            } else if (i > max) {
                max = i
            }
        }
        return (min, max)
    }
    
    // 返回函数
    return compire;
}
let scores = [1, 3, 2, 100, -19]

// 都不需要使用标签
let compire = getCompireFn()
let c = compire(scores)

print (c) // (min: -19, max: 100)
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
  • 函数作为参数:参数名也不能加标签
func getMax(_ scores: [Int]) -> Int {
    var max = scores[0]
    for i in scores {
        if (i > max) {
            max = i
        }
    }
    return max
}

func getMin(_ scores: [Int]) -> Int {
    var min = scores[0]
    for i in scores {
        if (i < min) {
            min = i
        }
    }
    return min
}

func compire(_ p1: Int, _ p2: Int, _ fn: ([Int]) -> Int) -> Int {
    return fn([p1, p2])
}

var ans = compire(1, 100, getMax)
print(ans)

ans = compire(1, 100, getMin)
print(ans)
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

# 三元表达式

let n1 = 1
let n2 = n1 % 2 == 0 ? n1 : 0

print(n2) // 0
1
2
3
4

# 闭包函数

func getSumFn (_ a: Int, _ b: Int)-> (Int) -> Int {
    func add(c: Int) -> Int {a + b + c}
    return add;
}


let sumFn = getSumFn(1, 2)
let c = sumFn(3)

print (c) // 6
1
2
3
4
5
6
7
8
9
10

# 自动闭包

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

// 函数闭包赋值,并没有执行
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 5

print("Now serving \(customerProvider())!")
// Now serving Chris!
print(customersInLine.count)
// 4
1
2
3
4
5
6
7
8
9
10
11

# 匿名闭包

  • 回调闭包
    let n1 = [1, 2, 3, 4]
    let n2 = n1.map({
        (n: Int) -> Int in
        return n * 2
    })
    
    // [2, 4, 6, 8]
    print(n2);
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 单行闭包简写
    let n1 = [1, 2, 3, 4]
    let n2 = n1.map({n in n % 2 == 0 ? n : 0})
    
    // [0, 2, 0, 4]
    print(n2);
    
    1
    2
    3
    4
    5
  • 闭包省略参数:通过位置操作
    let n1 = [1, 2, 3, 4]
    let n2 = n1.map({$0 % 2 == 0 ? $0 : 0})
    
    // [0, 2, 0, 4]
    print(n2);
    
    1
    2
    3
    4
    5

#

# 基础使用

  • 访问自己属性:self
  • 重写:父类方法同名override
  • 重载:自己方法函数参数或返回值不同
class Caculator {
    var a: Int?
    var b = 100
    init(_ a: Int?) {
        print("init")
        self.a = a
    }
    deinit {
        print("对象释放操作")
    }
    func add() -> Int {
        return self.a! + self.b
    }
    
    func add(c: Int) -> Int {
        return self.a! + self.b + c
    }
    
    // 重写父类方法用override
}

let n1 = Caculator(10)
print(n1.add()) // 110
print(n1.add(c: 1)) // 111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# getter / setter

  • getter/setter
    class Caculator {
        var a: Int?
    
        var b: Int {
            get {
                print("get")
                return self.a! * 2
            }
            set {
                print("set")
                self.a = newValue
            }
        }
        
        init(a: Int) {
            self.a = a
        }
    }
    
    let n1 = Caculator(a: 10)
    
    n1.b = 20
    print(n1.b) // 40
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
  • willSet / didSet 不能和getter同时使用
    class Caculator {
        var a: Int?
    
        var b: Int {
            willSet {
                print("willSet") // willSet
                print(newValue)  // 20
            }
            didSet {
                print("didSet")  // didSet
            }
        }
        
        init(a: Int) {
            self.a = a
            self.b = 100
        }
    }
    
    let n1 = Caculator(a: 10)
    
    n1.b = 20
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

# 结构体

  • 结构体和类的区别:
    • 结构体是传值,类是传引用
    • 类的实例用let设置成常量以后,里面的属性是可以改变的,结构体不行
struct People
{
    var name: String = "默认值"
    {
        willSet
        {
            print("将要改变:\(newValue)"); // 将要改变:hdy
        }
        didSet
        {
            print("已经改变:\(oldValue)"); // 已经改变:默认值
        }
    }
}

var p = People();

p.name = "hdy";
print(p.name); // hdy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 枚举

# 枚举固定类型

  • 二次赋值只需要.xxx
enum Barcode {
    case a, b, c
}

var code = Barcode.a

print(code); // a
print(code == Barcode.a); // true
print(type(of: code)); // Barcode


code = .b
print(code); // b
1
2
3
4
5
6
7
8
9
10
11
12
13

不会被隐式地赋值为 0,1,2 和 3。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的 CompassPoint 类型

# 使用枚举值

  • 枚举键:.rawValue
  • 枚举值
  • 枚举公用方法:在枚举中定义func
enum school: Int {
    case teacher = 1
    case two, three, four
    func getFive() -> String {
        switch self {
        case .teacher:
            return "老师好"
        case .two, .three, .four:
            return "234"
        }
    }
}
let rawValue = school.three.rawValue
print(rawValue) // 3

let ans = school.three.getFive()
print(ans) // 234
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 枚举自定义结构类型

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

let code = Barcode.upc(1, 2, 3, 4);

print(code); // upc(1, 2, 3, 4)
1
2
3
4
5
6
7
8

# 遍历枚举

  • 实现CaseIterable协议就能遍历
enum People: CaseIterable {
    case teacher;
    case student;
    case police;
}

print(People.allCases.count); // 3

for i in People.allCases {
    print(i); // teacher student police
}
1
2
3
4
5
6
7
8
9
10
11

# 并发性

# async

func connectUser(to server: String) async{
    async let userID = fetchUserID(from: server)
    async let username = fetchUsername(from: server)
    let greeting = await "Hello \(username), user ID \(userID)"
    print(greeting)
}
1
2
3
4
5
6
  • 使用Task调用异步函数
Task {
    await connectUser(to: "primary")
}
1
2
3

# 协议

  • 声明协议
    protocol Animal {
        var name: String { get }
    
        // mutating:可修改方法
        mutating func say()
    }
    
    1
    2
    3
    4
    5
    6
  • 遵循协议
    protocol Animal {
        var name: String { get }
    
        // mutating:可修改方法
        mutating func say()
    }
    
    class People: Animal {
        var name: String;
        init(_ name: String) {
            self.name = name
        }
        func say() {
            print("我是人,我的名字是\(self.name)")
        }
    }
    
    let A = People("小黄")
    A.say() // 我是人,我的名字是小黄
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

# Error

  • 最佳实践:声明错误类型
enum paramError: Error {
    case tooBig
    case tooSmall
    case typeErr
}
1
2
3
4
5
  • defer:defer 代码块来表示在函数返回前,函数中最后执行的代码。无论函数是否会抛出错误,这段代码都将执行。

enum paramError: Error {
    case tooBig
    case tooSmall
    case typeErr
}

func getScoreLevel(_ score: Int)throws -> String {
    defer {
        print("返回前最后执行的代码");
    }
    print("正常开始")
    if (score > 100) {
        throw paramError.tooBig
    } else if (score < 0) {
        throw paramError.tooSmall
    }
    switch score {
    case let s where s > 60:
        return "及格"
    case let s where s < 60:
        return "不及格"
    case let s where s == 60:
        return "刚刚及格"
    default:
        print("未知类型\(score)")
    }
    return ""
}

let ans = try getScoreLevel(100)
print(ans)

//正常开始
//返回前最后执行的代码
//及格
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

# 泛型

func repeatArray<Item>(item: Item, num: Int) -> [Item]{
    var ans: [Item] = [];
    for _ in 0..<num {
        ans.append(item)
    }
    return ans
}

let ans = repeatArray(item: "Me", num: 4)
print(ans); // ["Me", "Me", "Me", "Me"]
1
2
3
4
5
6
7
8
9
10

问题:泛型的where怎么用?where 来指定对类型的一系列需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
let ans = anyCommonElements([1, 2, 3], [3])
print(ans)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 检测api可用性

if #available(iOS 10, macOS 10.12, *) {
    // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
    // 使用先前版本的 iOS 和 macOS 的 API
}
1
2
3
4
5
上次更新: 11/1/2024