枚举、结构体、类都可以定义方法。
一、方法
方法分为:实例方法、类型方法。
- 实例方法: 通过实例调用
- 类型方法: 通过类型调用,用
static
或者class
关键字定义
示例代码:
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
可以允许这种修改行为。
示例代码(结构体):
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
会报错:
示例代码(枚举):
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
,可以消除函数调用后返回值未被使用的警告。
示例代码:
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
的语法类似于实例方法和计算属性的结合体,本质就是方法(函数)。
示例代码:
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
可以接受多个参数,并且类型是任意的
细节:
-
subscript
可以没有set
方法,但必须要有get
方法。
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
}
}
}
- 如果只有
get
方法,可以省略get
。
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
}
}
- 可以设置参数标签(设置标签后,标签名一定要带上,不能省略)。
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])
- 下标可以是类型方法。
class Point {
static subscript(row: Int, column: Int) -> String {
return "\(row)-\(column)"
}
}
print(Point[2, 3]) // 输出:2-3
结构体、类作为返回值对比:
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
方法:
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
方法,会发生什么变化呢?
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
,所以可以直接修改值。