【iOS】Swift基础语法(类与结构体、函数式)

旨在快速打通iOS开发流程

By yesmore on 2022-11-25
阅读时间 9 分钟
文章共 2k
阅读量

本章内容概览:Swift中的类与结构体、常用操作数组函数式方法

上一节:【iOS】01_Swift基础语法(简介、语法基础、基础类型介绍)

下一节:【iOS】03_Swift基础语法(控制流、集合)

同系列文章请查看:快乐码元 - iOS篇

Ⅰ.类和结构体

1.类

类可以定义属性、方法、构造器、下标操作。类使用扩展来扩展功能,遵循协议。类还可以继承,运行时检查实例类型。

与其他编程语言所不同的是,Swift 并不要求你为自定义类去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类,系统会自动生成面向其它代码的外部接口。

1.1 类和结构体对比

Swift 中类和结构体有很多共同点。共同处在于:

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义附属脚本用于访问值
  • 定义构造器用于生成初始化值
  • 通过扩展以增加默认实现的功能
  • 符合协议以对某类提供标准功能

与结构体相比,类还有如下的附加功能:

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 解构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class C {
var p: String
init(_ p: String) {
self.p = p
}

// 下标操作
subscript(s: String) -> String {
get {
return p + s
}
set {
p = s + newValue
}
}
}

let c = C("hi")
print(c.p)
print(c[" ming"])
c["k"] = "v"
print(c.p)

1.2 恒等运算符

因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。

为了能够判定两个常量或者变量是否引用同一个类实例,Swift 内建了两个恒等运算符:

恒等运算符 不恒等运算符
运算符为:=== 运算符为:!==
如果两个常量或者变量引用同一个类实例则返回 true 如果两个常量或者变量引用不同一个类实例则返回 true

1.3 结构体

我们可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能。

与 C 和 Objective C 不同的是:

  • 结构体不需要包含实现文件和接口。
  • 结构体允许我们创建一个单一文件,且系统会自动生成面向其它代码的外部接口。

结构体总是通过被复制的方式在代码中传递,因此它的值是不可修改的。

1
2
3
4
5
6
struct nameStruct { 
Definition 1
Definition 2
……
Definition N
}
1
2
3
4
5
6
7
8
9
10
11
12
struct MarksStruct {
var mark: Int

init(mark: Int) {
self.mark = mark
}
}
var aStruct = MarksStruct(mark: 98)
var bStruct = aStruct // aStruct 和 bStruct 是使用相同值的结构体!
bStruct.mark = 97
print(aStruct.mark) // 98
print(bStruct.mark) // 97

结构体是值类型,可以定义属性、方法、构造器、下标操作。结构体使用扩展来扩展功能,遵循协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct S {
var p1: String = ""
var p2: Int
}

extension S {
func f() -> String {
return p1 + String(p2)
}
}

var s = S(p2: 1)
s.p1 = "1"
print(s.f()) // 11

1.4 属性

类、结构体或枚举里的变量常量就是他们的属性。

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 S {
static let sp = "类型属性" // 类型属性通过类型本身访问,非实例访问
var p1: String = ""
var p2: Int = 1
// cp 是计算属性
var cp: Int {
get {
return p2 * 2
}
set {
p2 = newValue + 2
}
}
// 只有 getter 的是只读计算属性
var rcp: Int {
p2 * 4
}
}
print(S.sp)
print(S().cp) // 2
var s = S()
s.cp = 3
print(s.p2) // 5
print(S().rcp) // 4

willSet 和 didSet 是属性观察器,可以在属性值设置前后插入自己的逻辑处理。

键路径表达式作为函数:

1
2
3
4
5
6
7
8
9
10
struct S2 {
let p1: String
let p2: Int
}

let s2 = S2(p1: "one", p2: 1)
let s3 = S2(p1: "two", p2: 2)
let a1 = [s2, s3]
let a2 = a1.map(\.p1)
print(a2) // ["one", "two"]

1.5 方法

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 E: String {
case one, two, three
func showRawValue() {
print(rawValue)
}
}

let e = E.three
e.showRawValue() // three

// 可变的实例方法,使用 mutating 标记
struct S {
var p: String
mutating func addFullStopForP() {
p += "."
}
}
var s = S(p: "hi")
s.addFullStopForP()
print(s.p)

// 类方法
class C {
class func cf() {
print("类方法")
}
}

static和class关键字修饰的方法类似 OC 的类方法。static 可以修饰存储属性,而 class 不能;class 修饰的方法可以继承,而 static 不能。在协议中需用 static 来修饰。

静态下标方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 静态下标
struct S2 {
static var sp = [String: Int]()

static subscript(_ s: String, d: Int = 10) -> Int {
get {
return sp[s] ?? d
}
set {
sp[s] = newValue
}
}
}

S2["key1"] = 1
S2["key2"] = 2
print(S2["key2"]) // 2
print(S2["key3"]) // 10

自定义类型中实现了 callAsFunction() 的话,该类型的值就可以直接调用。

1
2
3
4
5
6
7
8
9
10
// callAsFunction()
struct S3 {
var p1: String

func callAsFunction() -> String {
return "show \(p1)"
}
}
let s2 = S3(p1: "hi")
print(s2()) // show hi

1.6 继承

类能继承另一个类,继承它的方法、属性等。

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 类继承
class C1 {
var p1: String
var cp1: String {
get {
return p1 + " like ATM"
}
set {
p1 = p1 + newValue
}
}
init(p1: String) {
self.p1 = p1
}
func sayHi() {
print("Hi! \(p1)")
}
}

class C2: C1 {
var p2: String
init(p2: String) {
self.p2 = p2
super.init(p1: p2 + "'s father")
}
}

C2(p2: "Lemon").sayHi() // Hi! Lemon's father

// 重写父类方法
class C3: C2 {
override func sayHi() {
print("Hi! \(p2)")
}
}

C3(p2: "Lemon").sayHi() // Hi! Lemon

// 重写计算属性
class C4: C1 {
override var cp1: String {
get {
return p1 + " like Out of the blade"
}
set {
p1 = p1 + newValue
}
}
}

print(C1(p1: "Lemon").cp1) // Lemon like ATM
print(C4(p1: "Lemon").cp1) // Lemon like Out of the blade

通过 final 关键字可以防止类被继承,final 还可以用于属性和方法。使用 super 关键字指代父类。

2.函数式

js:这个我熟

2.1 map

map 可以依次处理数组中元素,并返回一个处理后的新数组

1
2
3
4
5
let a1 = ["a", "b", "c"]
let a2 = a1.map {
"\($0)2"
}
print(a2) // ["a2", "b2", "c2"]

使用 compactMap 可以过滤 nil 的元素。flatMap 会将多个数组合成一个数组返回。

2.2 filter

根据指定条件返回

1
2
3
4
5
let a1 = ["a", "b", "c", "call my name"]
let a2 = a1.filter {
$0.prefix(1) == "c"
}
print(a2) // ["c", "call my name"]

2.3 reduce

reduce 可以将迭代中返回的结果用于下个迭代中,还能让你设个初始值。

1
2
3
4
5
6
7
let a1 = ["a", "b", "c", "call my name.", "get it?"]
let a2 = a1.reduce("Hey u,", { partialResult, s in
// partialResult 是前面返回的值,s 是遍历到当前的值
partialResult + " \(s)"
})

print(a2) // Hey u, a b c call my name. get it?

2.4 sorted

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
// 类型遵循 Comparable
let a1 = ["a", "b", "c", "call my name.", "get it?"]
let a2 = a1.sorted()
let a3 = a1.sorted(by: >)
let a4 = a1.sorted(by: <)

print(a2) // Hey u, a b c call my name. get it?
print(a3) // ["get it?", "call my name.", "c", "b", "a"]
print(a4) // ["a", "b", "c", "call my name.", "get it?"]

// 类型不遵循 Comparable
struct S {
var s: String
var i: Int
}
let a5 = [S(s: "a", i: 0), S(s: "b", i: 1), S(s: "c", i: 2)]
let a6 = a5
.sorted { l, r in
l.i > r.i
}
.map {
$0.i
}

print(a6) // [2, 1, 0]

参考资料:


Tips: Please indicate the source and original author when reprinting or quoting this article.