0326看到了集合

siwft笔记

引言-写给我自己在学习swift成为苹果开发者之前的猜想

怎么学习swift?如何制作并发布一个由swift制作的软件并发布到ios商店,首先我在知乎等渠道已经知道开发者的一些内容,并且知道了swift和c-sharp的选择问题。重点是怎么学?我这里想的是可以先从国内别人整理好的教程先速通一遍,然后在苹果开发者网站学习新的swift特性

我的目标是什么?我的时间可太宝贵了,做这个事一定事出有因并且有所成就,我的目标很简单,就是完成一个成品软件,在竞争的时候可以展出,可以顶替一个实习经历。

学习链接:

apple开发者的官方教程

apple的官方中文教程

菜鸟教程

swift编程语言中文教程

[youtube上一个声音很好听的教学up](https://www.youtube.com/ChaoCode 博主)

如何阅读Apple developer网站

这是主页

首页的讲座、WWDC、开发者你好(其实指向新闻hello)、开发者见面(其实指向events,是苹果最近的闲暇过从)是单独一个网页

顶部探索 和 首页pathway 和pathway下面的每个 模块指向同一个位置(ios18 visionOS2等)

面向开发者的新内容其实是whatsnew,指向一个单独的whatsnew网页,里面的内容其实就是pathway里的模块

开发者工程

这里是视频,大多数是WWDC的

也就是说其实重要的是pathway下面的模块,有时候也可以看看WWDC每次不同的专题。我的评价是直接看底下的模块

image-20250326113552399

swift编程语言中文教程的学习笔记

变量声明

常量和变量

let 来声明常量编译的时候,并不需要有明确的值,但是你只能为它赋值一次。

var 来声明变量

  • 编译器会自动推断其类型

  • 初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割

  • 值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,需要显式转换。

  • var myVariable = 42
    myVariable = 50
    let explicitDouble: Double = 70
    let widthLabel = label + String(explicitDouble)
    var x = 0.0, y = 0.0, z = 0.0
    // 常量和变量名可以包含几乎所有的字符
    let π = 3.14159
    let 你好 = "你好世界"
    let 🐶🐮 = "dogcow"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    ## 变量类型

    ```
    Int整型值
    Double 和 Float浮点型值64位32位,Double 精确度很高,至少有 15 位小数,而 Float 只有 6 位小数
    Bool布尔型值
    String文本型数据
    Swift 还提供了三个基本的集合类型,Array、Set和 Dictionary
    Tuple元组
    Optional可选
    UInt无符号类型
    ```

    ### 字符串

    * 转义字符 `\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)
    * **使用#防止转义**,打印字符串文字 `#"Line 1 \nLine 2"#` 会打印换行符转义序列(`\n`)而不是给文字换行。

    * 更简单的**把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠(\)**

    ~~~swift
    let apples = 3
    let appleSummary = "I have \(apples) apples."
  • 使用三个双引号(""")来包含多行字符串内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 这种里面是有分行的
    let quotation = """
    I said "I have \(apples) apples."
    And then I said "I have \(apples + oranges) pieces of fruit."
    """
    // 这种里面没有分行,回车处是反斜线
    let softWrappedQuotation = """
    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."
    """
  • 一些函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 初始化空字符串
    var emptyString = "" // 空字符串字面量
    var anotherEmptyString = String() // 初始化方法
    if emptyString.isEmpty { //isEmpty 属性来判断该字符串是否为空
    print("Nothing to see here")
    }
    let exclamationMark: Character = "!"
    welcome.append(exclamationMark) //append() 方法将一个字符附加到一个字符串变量的尾部

    let string1 = "hello"
    let string2 = " there"
    var welcome = string1 + string2

    var word = "cafe"
    print("the number of characters in \(word) is \(word.count)") //获得一个字符串中 Character 值的数量,可以使用 count 属性

数组

  • 使用方括号 [] 来创建数组和字典,并使用下标或者键(key)来访问元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 这个是数组
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
shoppingList.append("blue paint")

var shoppingList: [String] = ["Eggs", "Milk"]

// 组合两个已存在的相同类型数组
var sixDoubles = threeDoubles + anotherThreeDoubles
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]

// 访问修改
shoppingList[4...6] = ["Bananas", "Apples"]
shoppingList.insert("Maple Syrup", at: 0) //调用数组的 insert(_:at:) 方法在某个指定索引值之前添加数据项
let mapleSyrup = shoppingList.remove(at: 0)//移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
  • 创建一个空数组
1
2
let emptyArray: [String] = []
shoppingList = []

字典

  • 使用方括号 [] 来创建数组和字典,并使用下标或者键(key)来访问元素。

    1
    2
    3
    4
    5
    6
    // 这个是字典
    var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
    ]
    occupations["Jayne"] = "Public Relations"
  • 创建一个空字典

    1
    2
    let emptyDictionary: [String: Float] = [:]
    occupations = [:]

bool

1
2
let orangesAreOrange = true
let turnipsAreDelicious = false

元组

*元组(tuples)*把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。

1
2
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")
  • 将一个元组的内容分解(decompose)成单独的常量和变量
1
2
3
4
5
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”
  • 如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记:
1
2
3
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 输出“The status code is 404”
  • 通过下标来访问元组中的单个元素,下标从零开始:
1
2
3
4
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”
  • 在定义元组的时候给单个元素命名:
1
let http200Status = (statusCode: 200, description: "OK")
  • 给元组中的元素命名后,你可以通过名字来获取这些元素的值:
1
2
3
4
print("The status code is \(http200Status.statusCode)")
// 输出“The status code is 200”
print("The status message is \(http200Status.description)")
// 输出“The status message is OK”

运算符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 赋值
let (x, y) = (1, 2)
// 计算
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
// 求余运算符(%)在其他语言也叫取模运算符
9 % 4 // 等于 1
// 一元负号运算符
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
// 表达式 a += 2 是 a = a + 2 的简写
等于(a == b)
不等于(a != b)
大于(a > b)
小于(a < b)
大于等于(a >= b)
小于等于(a <= b)
  • 三元运算符号问题 ? 答案 1 : 答案 2。它简洁地表达根据 问题成立与否作出二选一的操作。如果 问题 成立,返回 答案 1 的结果;反之返回 答案 2 的结果。

区间运算符(Range Operators)

Swift 提供了几种方便表达一个区间的值的区间运算符

闭区间运算符

闭区间运算符a...b)定义一个包含从 ab(包括 ab)的所有值的区间。

1
2
3
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}

半开区间运算符

半开区间运算符a..<b)定义一个从 ab 但不包括 b 的区间。 之所以称为半开区间

1
2
3
for i in 0..<count {
print("第 \(i + 1) 个人叫 \(names[i])")
}

单侧区间

闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间

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

代码规范

不需要 main() 函数。你同样不需要在每个语句结尾写上分号

分号

Swift 并不强制要求你在每条语句的结尾处使用分号(;

有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:

1
2
let cat = "🐱"; print(cat)
// 输出“🐱”

输出常量和变量

函数全名print(_:separator:terminator:)

  • 用 Xcode,print(_:separator:terminator:) 将会输出内容到“console”面板上。

  • 该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 terminator 参数–例如,print(someValue, terminator:"")

  • //将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义
    print("The current value of friendlyWelcome is \(friendlyWelcome)")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    ### 注释

    ```swift
    // 这是一个注释

    /* 这也是一个注释,
    但是是多行的 */

    // 以下是一个嵌套注释,套了一层
    /* 这是第一个多行注释的开头
    /* 这是第二个被嵌套的多行注释 */
    这是第一个多行注释的结尾 */

数值型字面量

整数字面量可以被写作:

  • 一个十进制数,没有前缀

  • 一个二进制数,前缀是 0b

  • 一个八进制数,前缀是 0o

  • 一个十六进制数,前缀是 0x

  • let decimalInteger = 17
    let binaryInteger = 0b10001       // 二进制的17
    let octalInteger = 0o21           // 八进制的17
    let hexadecimalInteger = 0x11     // 十六进制的17
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    - 十进制浮点数可有一个可选的指数(exponent),通过大写或者小写的 `e` 来指定

    - `1.25e2` 表示 1.25 × 10^2,等于 `125.0`。
    - `1.25e-2` 表示 1.25 × 10^-2,等于 `0.0125`

    * 十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定
    - `0xFp2` 表示 15 × 2^2,等于 `60.0`。
    - `0xFp-2` 表示 15 × 2^-2,等于 `3.75`。

    ### 类型别名

    使用 `typealias` 关键字来定义类型别名。

    ```swift
    typealias AudioSample = UInt16

控制流

if

强制解析!

当你确定可选类型确实包含值之后,可以在可选的名字后面加一个感叹号(!)来获取值。

这个惊叹号表示“我知道这个可选有值,请使用它。这被称为可选值的强制解析(forced unwrapping):

1
2
3
4
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 输出“convertedNumber has an integer value of 123.”

for-in

1
2
3
4
5
6
7
8
9
10
11
12
13
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}

Switch

运行 switch 中匹配到的 case 语句之后,程序会退出 switch 语句,并不会继续向下运行,所以不需要在每个子句结尾写 break

需要default

1
2
3
4
5
6
7
8
9
10
11
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}

for

1
2
3
4
5
6
7
8
9
10
var total = 0
// ..< 创建的范围不包含上界
for i in 0..<4 {
total += i
}
// 如果想包含的话需要使用 ...
for i in 0...4 {
total += i
}
print(total)

可选值optional

  • 一个可选值的声明方法:**在类型后面加一个问号(?)来标记这个变量的值是可选的。**一个可选的 Int 被写作 Int? 而不是 Int

  • 声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil

  • 可选值有缺失值nil和有具体的值两种,如果声明了则有值。

  • var optionalString: String? = "Hello"
    let nickName: String? = nil
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    ### 可选绑定

    使用*可选绑定(optional binding)*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if` 和 `while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。``

    **用let进行赋值,如果值是nil则返回false**,如果不是 `nil`,会将值解包并赋给 `let` 后面的常量,代码块中就可以使用这个值了。

    ```swift
    if let name = optionalName {
    greeting = "Hello, \(name)"
    }

  • 使用 ?? 操作符来提供一个默认值。如果可选值缺失的话,可以使用默认值来代替。

  • let nickName: String? = nil
    let fullName: String = "John Appleseed"
    # nil则取后面fullname的值
    let informalGreeting = "Hi \(nickName ?? fullName)"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    * 处理变量的可选值时可以在操作(比如方法、属性和子脚本)之前加 `?`。

    * 如果 `?` 之前的值是 `nil`,`?` 后面的东西都会被忽略,并且整个表达式返回 `nil`。
    * 不是nil,可选值会被解包,之后的所有代码都会按照解包后的值运行。

    * ```swift
    // 这个返回的是真的解包值,就是2.5
    let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
    let sideLength = optionalSquare?.sideLength
    // 这个返回的的就是nil
    let optionalSquare: Square? = nil
    let sideLength = optionalSquare?.sideLength

强制解析!

当你确定可选类型确实包含值之后,可以在可选的名字后面加一个感叹号(!)来获取值。

这个惊叹号表示“我知道这个可选有值,请使用它。这被称为可选值的强制解析(forced unwrapping):

1
2
3
4
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 输出“convertedNumber has an integer value of 123.”

函数

  • 使用 func 来声明一个函数,使用名字和参数来调用函数。使用 -> 来指定函数返回值的类型。

  • 默认情况下,函数使用它们的参数名称作为它们参数的标签,也可以换一个参数标签:在参数名称前可以自定义参数标签,或者使用 _ 表示不使用参数标签。

  • // 1. 默认用参数名称作为标签
    func greet(person: String, day: String) -> String {
        return "Hello \(person), today is \(day)."
    }
    greet(person:"Bob", day: "Tuesday")
    
    // 2. 这样调用的时候直接写自定义标签
    func greet(_ person: String, on day: String) -> String {
        return "Hello \(person), today is \(day)."
    }
    greet("John", on: "Wednesday")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    * 用元组返回多个值,函数参数是数组的传参方法

    ```swift
    func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
    if score > max {
    max = score
    } else if score < min {
    min = score
    }
    sum += score
    }

    return (min, max, sum)
    }
    let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9])
    // 这里看到是一个元组
    print(statistics.sum)
    print(statistics.2)
  • 函数也可以作为参数返回,可这样写:((Int) -> Int) ,也可不打括号

    1
    2
    3
    4
    5
    6
    7
    8
    func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
    return 1 + number
    }
    return addOne
    }
    var increment = makeIncrementer()
    increment(7)
  • 函数也可以当做参数传入另一个函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
    if condition(item) {
    return true
    }
    }
    return false
    }
    func lessThanTen(number: Int) -> Bool {
    return number < 10
    }
    var numbers = [20, 19, 7, 12]
    hasAnyMatches(list: numbers, condition: lessThanTen)

闭包

  • 用in定义的就是闭包

  • 使用 {} 包起来一整个函数来创建一个匿名闭包。使用 in 将参数和返回值类型的声明与闭包函数体进行分离。

1
2
3
4
5
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
  • 如果一个闭包的类型已知,你可以忽略参数,返回值,甚至两个都忽略。单个语句闭包会把它语句的值当做结果返回。

  • let mappedNumbers = numbers.map({ number in 3 * number })
    print(mappedNumbers)
    
    
    1
    2
    3
    4
    5
    6
    7

    * 通过参数位置$0,$1而不是参数名字来引用参数

    ```swift
    let sortedNumbers = numbers.sorted ({ $0 > $1 })
    // 这里因为是最后一个参数,闭包作为参数,可以把括号也忽略
    let sortedNumbers = numbers.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
// 子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。
class Shape: NamedShape {
var numberOfSides: Int = 0
var name: String
//使用 init 来创建一个构造器
// self 被用来区别实例变量 name 和构造器的参数 name
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
// 重写父类的方法的话,需要用 override 标记
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
// 创建一个类的实例,在类名后面加上括号。
// 使用点语法来访问实例的属性和方法。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

setter和getter特性,这个没看懂

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
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0

init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}

var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}

override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)

willSet和 didSet特性,这个买看懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)

枚举,这个买看懂

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
enum Rank: Int {
// 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。
//Ace 被显式赋值为 1,并且剩下的原始值会按照顺序赋值。
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
// 使用 rawValue 属性来访问一个枚举成员的原始值。
let aceRawValue = ace.rawValue
// 从原始值创建一个枚举实例。如果存在与原始值相应的枚举成员就返回该枚举成员,否则就返回 nil。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}

协议和扩展

使用 protocol 来声明一个协议。

1
2
3
4
5
protocol ExampleProtocol {
var simpleDescription: String { get }
// mutating 关键字用来标记一个会修改结构体的方法。
mutating func adjust()
}

类、枚举和结构体都可以遵循协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

使用 extension 来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展让某个在别处声明的类型(下面的int)来遵守某个协议,这同样适用于从外部库或者框架引入的类型。

1
2
3
4
5
6
7
8
9
10
11
// 这里让int类型满足ExampleProtocol协议,并定义了协议需要的两个函数
// int是别的地方定义的,可加上extension之后让他满足协议
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)

错误处理

1
2
3
4
5
6
7
8
9
10
11
//  Error是个协议
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
//使用 throw 来抛出一个错误和使用 throws 来表示一个可以抛出错误的函数。
// 如果在函数中抛出一个错误,这个函数会立刻返回并且调用该函数的代码会进行错误处理。
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}

有多种方式可以用来进行错误处理。

一种方式是使用 do-catch 。在 do 代码块中,使用 try 来标记可以抛出错误的代码。在 catch 代码块中,除非你另外命名,否则错误会自动命名为 error

1
2
3
4
5
6
7
8
9
10
11
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
}catch {
// 在 `catch` 代码块中,除非你另外命名,否则错误会自动命名为 `error` 。
print(error)
}

另一种处理错误的方式使用 try? 将结果转换为可选的。如果函数抛出错误,该错误会被抛弃并且结果为 nil。否则,结果会是一个包含函数返回值的可选值。

1
2
3
4
// send抛出错误则printerSuccess为nil
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
// send不抛出错误则printerSuccess为send返回值但是是可选类型
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

使用 defer 代码块来表示在函数返回前,函数中最后执行的代码。无论函数是否会抛出错误,这段代码都将执行。使用 defer,可以把函数调用之初就要执行的代码和函数调用结束时的扫尾代码写在一起,虽然这两者的执行时机截然不同。这个没看懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}

let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)

泛型

在尖括号里写一个名字来创建一个泛型函数或者类型。

1
2
3
4
5
6
7
8
9
// Item是一个不知道什么的类型
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result: [Item] = []
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
makeArray(repeating: "knock", numberOfTimes: 4)

where

在类型名后面使用 where 来指定对类型的一系列需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。

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

断言

可以调用 Swift 标准库的 assert(_:_:file:line:) 函数来写一个断言。

表达式的结果为 false 的时候这条信息会被显示:

1
2
3
4
let age = -3
// 在这个例子中,只有 `age >= 0` 为 `true` 时,即 `age` 的值非负的时候,代码才会继续执行。如果 `age` 的值是负数,就像代码中那样,`age >= 0` 为 `false`,断言被触发,终止应用。
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触发

如果不需要断言信息,可以就像这样忽略掉:

1
assert(age >= 0)

以下是swift Documentation的swift tour部分,后面的没有看,也不需要看,因为已经通过菜鸟教程看过了

https://docs.swift.org/swift-book/documentation
Property Wrappers包裹属性,比如@binding @envirYou don’t need to import a separate library for functionality like outputting text or handling strings. Code written at global scope is used as the entry point for the program, so you don’t need a main() function. You also don’t need to write semicolons at the end of every statement.

Use let to make a constant and var to make a variable.
Providing a value when you create a constant or variable lets the compiler infer its type. If the initial value doesn’t provide enough information (or if there isn’t an initial value), specify the type by writing it after the variable, separated by a colononment @state @observedState都是包裹属性,这样的带有包裹属性的变量都有$Value _Value两种,
@state:只要这个数字改变,则画面重新生成,每一个warrp的
@binding,如果一个srtuct含有binding修饰的变量则创建的时候必须要要把binding进行赋值
对于占用多行的字符串,使用三个星期双引号(“”)。删除每个引号开头的缩进,只要它与关闭引号的缩进相匹配。例如:

If you’re assigning an empty array or dictionary to a new variable, or another place where there isn’t any type information, you need to specify the type.
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]

Another way to handle optional values is to provide a default value using the ?? operator. If the optional value is missing, the default value is used instead.
let nickname: String? = nil
let fullName: String = “John Appleseed”
let informalGreeting = “Hi (nickname ?? fullName)”

write _ to use no argument label.
func greet(_ person: String, on day: String) -> String {
return “Hello (person), today is (day).”
}
greet(“John”, on: “Wednesday”)

Use a tuple to make a compound value — for example, to return multiple values from a function. The elements of a tuple can be referred to either by name or by number.
函数变量,函数作为形参
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)

A function can take another function as one of its arguments.
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool
condition is a function

closures不就是闭包么
突然想起来c++也有闭包 lamdba

You can write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments and return type from the body.
numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})

Override覆盖超类实现的子类上的方法被标记为覆盖——在没有覆盖的情况下意外覆盖方法被编译器检测为错误。编译器还检测具有覆盖的方法,这些方法实际上不会覆盖超类中的任何方法。

没有看懂class中willset和didset的使用,没看懂rawvalue的使用

结构和类之间最重要的区别之一是,结构在代码中传递时总是被复制,但类是通过引用传递的。

使用async来标记异步运行的函数。通过在异步函数前面写上await来标记对异步函数的调用。使用asynclet调用异步函数,让它与其他异步代码并行运行。当您使用它返回的值时,请写await。
使用Task{}从同步代码中调用异步函数,无需等待它们返回。

没看懂
Actor和class相同:只是actor确保不同的异步函数可以同时与同一actor的实例安全交互,当您在actor上调用方法或访问其属性之一时,您将该代码标记为等待,以表明它可能必须等待已经在actor上运行的其他代码完成。

You represent errors using any type that adopts the Error protocol.
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
throw PrinterError.noToner
Use throw to throw an error and throws to mark a function that can throw an error. If you throw an error in a function, the function returns immediately and the code that called the function handles the error

有几种方法可以处理错误。一种方法是使用do-catch。在do块内,你通过在代码前面写上try来标记可以抛出错误的代码。在catch块中,错误会自动被赋予名称错误You can provide multiple catch blocks that handle specific errors.
没看懂另一种处理错误的方法是使用try?将结果转换为可选结果。如果函数抛出错误,则丢弃特定错误,结果为零。否则,结果是包含函数返回的值的可选值。

没看懂
使用defer编写一个代码块,该代码块在函数中所有其他代码之后执行,就在函数返回之前。无论函数是否抛出错误,代码都会被执行。您可以使用defer来并排编写设置和清理代码,即使它们需要在不同的时间执行。

在角括号内写一个名称,以制作一个通用函数或类型。

在正文前使用where来指定要求列表——例如,要求类型实现协议,要求两种类型相同,或要求一个类具有特定的超类。
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Element: Equatable, T.Element == U.Element{}

函数的第一个形参前面又个人_,就可以不用写lhs:,而是直接加实惨就可以