1. 实例方法(Instance Methods)
- 实例方法是属于某个特定类、结构体或者枚举类型实例的方法。
- 实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。
- 实例方法的语法与函数完全一致。
- 实例方法要写在它所属的类型的前后大括号之间。
- 实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。
- 实例方法只能被它所属的类的某个特定实例调用。
- 实例方法不能脱离于现存的实例而被调用。
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
Counter 类定义了三个实例方法:
increment 让计数器按一递增;
increment(by: Int) 让计数器按一个指定的整数值递增;
reset 将计数器重置为0。
- 和调用属性一样,用点语法(dot syntax)调用实例方法:
let counter = Counter() // 初始计数值是0
counter.increment() // 计数值现在是1
counter.increment(by: 5) // 计数值现在是6
counter.reset() // 计数值现在是0
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用)。
方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
1.1 self 属性
- 类型的每一个实例都有一个隐含属性叫做 self,self 完全等同于该实例本身。
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
} // 打印“This point is to the right of the line where x == 1.0”
1.2 在实例方法中修改值类型
- 结构体和枚举是值类型。
- 默认情况下,值类型的属性不能在它的实例方法中被修改。
- 但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择 可变(mutating)行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。
- 方法还可以给它隐含的 self 属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
- 要使用 可变方法,将关键字 mutating 放到方法的 func 关键字之前就可以了:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印“The point is now at (3.0, 4.0)”
上面的 Point 结构体定义了一个可变方法 moveBy(x:y :) 来移动 Point 实例到给定的位置。
该方法被调用时修改了这个点,而不是返回一个新的点。
方法定义时加上了 mutating 关键字,从而允许修改属性。
注意:
不能在结构体类型的常量(a constant of structure type)上调用可变方法,因为其属性不能被改变,即使属性是变量属性。
1.3 在可变方法中给 self 赋值
- 可变方法能够赋给隐含属性 self 一个全新的实例:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
可变方法 moveBy(x:y:) 创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。
- 枚举的可变方法可以把 self 设置为同一枚举类型中不同的成员:
enum TriStateSwitch {
case off, low, high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight 现在等于 .high
ovenLight.next()
// ovenLight 现在等于 .off
上面的例子中定义了一个三态切换的枚举。每次调用 next() 方法时,开关在不同的电源状态(off, low, high)之间循环切换。
2. 类型方法
可以定义在类型本身上调用的方法,这种方法就叫做类型方法。
在方法的 func 关键字之前加上关键字 static,来指定类型方法。
-
类还可以用关键字 class 来指定,从而允许子类重写父类该方法的实现。
在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。 每一个类型方法都被它所支持的类型显式包含。
class SomeClass {
class func someTypeMethod() {
// 在这里实现类型方法
}
}
SomeClass.someTypeMethod()
- 在类型方法的方法体(body)中,self 属性指向这个类型本身,而不是类型的某个实例。
- 可以用 self 来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
struct LevelTracker {
static var highestUnlockedLevel = 1 // 监测玩家已解锁的最高等级
var currentLevel = 1 // 监测每个玩家当前的等级
static func unlock(_ level: Int) { // 一旦新等级被解锁,它会更新 highestUnlockedLevel 的值
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func isUnlocked(_ level: Int) -> Bool { // 如果某个给定的等级已经被解锁,它将返回 true
return level <= highestUnlockedLevel
}
@discardableResult
mutating func advance(to level: Int) -> Bool { // 这个方法会在更新 currentLevel 之前检查所请求的新等级是否已经解锁。
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
定义了一个名为 LevelTracker 结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。LevelTracker 结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
注意:
1. 因为允许在调用 advance(to:) 时候忽略返回值,不会产生编译警告,所以函数被标注为 @discardableResult 属性。
2. 使用 discardableResult 特性声明的函数,表明该函数虽然有返回值,但如果没有使用该返回值,编译器不会产生警告。
- Player 类使用 LevelTracker 来监测和更新每个玩家的发展进度:
class Player {
var tracker = LevelTracker()
let playerName: String
func complete(level: Int) {
LevelTracker.unlock(level + 1)
tracker.advance(to: level + 1)
}
init(name: String) {
playerName = name
}
}
- 可以为一个新的玩家创建一个 Player 的实例,然后看这个玩家完成等级一时发生了什么:
var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")// 打印“highest unlocked level is now 2”
- 如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败:
player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
print("player is now on level 6")
} else {
print("level 6 has not yet been unlocked")
} // 打印“level 6 has not yet been unlocked”