方法是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法和类型方法。与 Objective-C 不同,除了类,结构体和枚举也能够定义方法。
实例方法 (Instance Methods)
实例方法 是属于某个特定类、结构体或者枚举类型实例的方法,语法与函数完全一致。
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
self 属性
类型的每一个实例都有一个隐含属性叫做self
,self
完全等同于该实例本身。
func increment() {
self.count += 1
}
没有明确地写self
,Swift 假定你是指当前实例的属性或者方法。
当实例方法的某个参数名称与实例的某个属性名称相同的时候,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用self
属性来区分参数名称和属性名称。
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOfX(_ x: Double) -> Bool {
return self.x > x
}
}
如果不使用self
前缀,Swift 就认为两次使用的x
都指的是名称为x
的函数参数。
在实例方法中修改值类型
结构体和枚举是 值类型。默认情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择可变(mutating)
行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的self
属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
要使用可变
方法,将关键字mutating
放到方法的func
关键字之前就可以了:
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(_ deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印 "The point is now at (3.0, 4.0)"
注意,不能在结构体类型的常量上调用可变方法,因为其属性不能被改变,即使属性是变量属性。
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// 这里将会报告一个错误
在可变方法中给 self 赋值
可变方法能够赋给隐含属性self
一个全新的实例。上面Point
的例子可以用下面的方式改写:
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)
}
}
枚举的可变方法可以把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
)之间循环切换。
类型方法
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做类型方法。在方法的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) {
if level > highestUnlockedLevel {
highestUnlockedLevel = level
}
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
@discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
下面,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
}
}
advance(to:)
忽略了返回的布尔值,因为之前调用LevelTracker.unlock(_:)
时就知道了这个等级已经被解锁了。
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"