【iOS】Swift系列十一 - 方法

枚举、结构体、类都可以定义方法。

一、方法

方法分为:实例方法、类型方法。

  • 实例方法: 通过实例调用
  • 类型方法: 通过类型调用,用static或者class关键字定义

示例代码:

1
2
3
4
5
6
7
8
9
10
11
class Car {
static var count = 0
init() {
Car.count += 1
}
static func getCount() -> Int { count }
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.getCount()) // 输出:3

注意:self在实例方法中代表实例,在类型方法中代表类型。

在上面示例代码中,类型方法getCount返回的count等价于self.count、Car.self.count、Car.count

二、mutating

结构体和枚举是值类型,默认情况下,值类型的属性不能被自身的实例方法修改。

func关键字前加mutating可以允许这种修改行为。

示例代码(结构体):

1
2
3
4
5
6
7
8
9
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(deltaX: Double, deltaY: Double) {
x += deltaX
y += deltaY
// 等价下面的代码
// self = Point(x: x + deltaX, y: y + deltaY)
}
}

如果不加mutating会报错:

示例代码(枚举):

1
2
3
4
5
6
7
8
9
10
11
12
13
enum StateSwitch {
case low, middle, high
mutating func next() {
switch self {
case .low:
self = .middle
case .middle:
self = .high
case .high:
self = .low
}
}
}

如果不加mutating会报错:

三、@discardableResult

func前面加个@discardableResult,可以消除函数调用后返回值未被使用的警告。

示例代码:

1
2
3
4
5
6
7
8
9
struct Point {
var x = 0.0, y = 0.0
@discardableResult mutating func moveX(deltaX: Double) -> Double {
x += deltaX
return x
}
}
var p = Point()
p.moveX(deltaX: 10)

如果不加@discardableResult会警告:

四、下标(subscript)

html中有标签元素:上标<sup></sup>下标<sub></sub>,经常用来表示数学符号。

Swift中使用subscript可以给任意类型(枚举、结构体、类)增加下标功能,有些地方也翻译为下标脚本

subscript的语法类似于实例方法和计算属性的结合体,本质就是方法(函数)。

示例代码:

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
class Point {
var x = 0.0, y = 0.0
subscript(index: Int) -> Double {
set {
if index == 0 {
x = newValue
} else if index == 1 {
y = newValue
}
}
get {
if index == 0 {
return x
} else if index == 1 {
return y
}
return 0
}
}
}
var p = Point()
p[0] = 11.1
p[1] = 22.2
print(p.x) // 输出:11.1
print(p.y) // 输出:22.2
print(p[0]) // 输出:11.1
print(p[1]) // 输出:22.2

如果不写subscript会报错:

特点:

  • subscript中定义的返回值类型决定了:
    • get方法的返回值类型;
    • set方法中newValue的类型
  • subscript可以接受多个参数,并且类型是任意的

细节:

  1. subscript可以没有set方法,但必须要有get方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
    get {
    if index == 0 {
    return x
    } else if index == 1 {
    return y
    }
    return 0
    }
    }
    }
  2. 如果只有get方法,可以省略get

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
    if index == 0 {
    return x
    } else if index == 1 {
    return y
    }
    return 0
    }
    }
  3. 可以设置参数标签(设置标签后,标签名一定要带上,不能省略)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Point {
    var x = 0.0, y = 0.0
    subscript(index i: Int) -> Double {
    if i == 0 {
    return x
    } else if i == 1 {
    return y
    }
    return 0
    }
    }
    print(p[index: 1])
  4. 下标可以是类型方法。

    1
    2
    3
    4
    5
    6
    class Point {
    static subscript(row: Int, column: Int) -> String {
    return "\(row)-\(column)"
    }
    }
    print(Point[2, 3]) // 输出:2-3

结构体、类作为返回值对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point {
var x = 10, y = 10
}

class PointManager {
var point = Point()
subscript(index: Int) -> Point {
get { point }
}
}
var pm = PointManager()
print(pm[0].x) // 输出:10
print(pm.point) // 输出:Point(x: 10, y: 10)

上面的代码,如果要对下标赋值,则需要添加set方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
class PointManager {
var point = Point()
subscript(index: Int) -> Point {
set { point = newValue }
get { point }
}
}
var pm = PointManager()
print(pm[0].x) // 输出:10
print(pm.point) // 输出:Point(x: 10, y: 10)
pm[0].x = 11
print(pm[0].x) // 输出:11
print(pm.point) // 输出:Point(x: 11, y: 10)

注意:pm[0].x = 11等价于pm[0] = Point(x: 11, y: pm[0].y)

如果把上面的Point由结构体(Struct)修改为类(Class),并且不写set方法,会发生什么变化呢?

1
2
3
4
5
6
7
8
9
10
11
12
class Point {
var x = 10, y = 10
}

class PointManager {
var point = Point()
subscript(index: Int) -> Point {
get { point }
}
}
var pm = PointManager()
pm[0].x = 11

编译完美通过,为什么呢?

  • 如果是值类型,pm[0]返回的是临时变量,内部肯定无法修改外面的变量,所以如果要修改值就需要加上set方法。
  • 如果是引用类型,pm[0]返回的就是point指针变量,pm[0].x变为point.x,所以可以直接修改值。