第11章:方法
方法是与特定类型相关联的函数。类,结构和枚举都可以定义实例方法,这些方法封装了用于处理给定类型的实例的特定任务和功能。类,结构和枚举也可以定义类型方法,它们与类型本身相关联。类型方法类似于Objective-C中的类方法。
结构和枚举可以在Swift中定义方法的事实是与C和Objective-C的主要区别。在Objective-C中,类是唯一可以定义方法的类型。在Swift中,您可以选择是定义类,结构还是枚举,并且仍然可以灵活地在您创建的类型上定义方法。
11.1 实例方法
实例方法是作用于特定类,结构或枚举的实例的函数。它们通过提供访问和修改实例属性的方法,或通过提供与实例目的相关的功能来支持这些实例的功能。实例方法具有的语法与函数的语法相同。在其所属类型的开始和结束括号内编写实例方法。实例方法可以隐式访问该类型的所有其他实例方法和属性。实例方法只能在其所属类型的特定实例上调用。如果没有现有实例,则无法单独调用它。
这是一个定义简单Counter
类的示例,可用于计算操作发生的次数:
class Counter {
var count = 0
// 这里的count是指某个具体实例中的count属性
// 如果想要获得指代该具体实例,则可以使用self关键字
/*
此方法与下面的方法等价
func increment() {
self.count += 1
}
实际上如果没有歧义可以如下面例子所写的那样省略self关键字
*/
func increment() {
count += 1
}
// 方法的参数可以具有参数名和标签
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
// 使用与属性相同的点语法调用实例方法
let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter's value is now 1
counter.increment(by: 5)
// the counter's value is now 6
counter.reset()
// the counter's value is now 0
struct Point {
var x = 0.0, y = 0.0
// 此示例展示了必须写self关键字的情形
// 由于isToTheRightOf的参数名是x,如果不写self,那么第一个x也会被认为是参数x,而不是实例变量x
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")
}
// Prints "This point is to the right of the line where x == 1.0"
通过以上示例还能够得出如下结论:
在Swift进行符号查找的过程中参数名称的优先级高于实例变量的优先级
结构和枚举是值类型。默认情况下,无法在其实例方法中修改值类型的属性。
但是,如果需要在特定方法中修改结构或枚举的属性,则可以选择改变该方法的行为。然后,该方法可以从方法中改变(即更改)其属性,并且当方法结束时,它所做的任何更改都将写回原始结构。该方法还可以为其隐式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
}
}
// 请注意:这里somePoint被定义为变量,所以可以修改其属性
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))")
// Prints "The point is now at (3.0, 4.0)"
/*
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0) 这将有报错导致编译不过
fixedPoint被定义为常量,则不能在结构类型的常量上调用修改方法,因为它的属性不能更改,即使它们是变量属性
*/
上面示例中可以修改属性的方法的本质是创建一个新的实例,然后把新的实例赋值给当前实例,代码如下:
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 is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
11.2 类型方法
如上所述,实例方法是在特定类型的实例上调用的方法。您还可以定义在类型本身上调用的方法。这些方法称为类型方法。通过把static关键字写在方法func关键字之前来指示类型方法。类也可以使用class关键字来指定允许子类覆盖超类的类型方法的实现。
在Swift中,您可以为所有类,结构和枚举定义类型级方法。每种类型方法都明确限定为它支持的类型。
使用点语法调用类型方法。但是,您在类型上调用类型方法,而不是在该类型的实例上调用。以下是在类调用上调用类型方法的方法SomeClass:
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
在类型方法的主体内,隐式self属性引用类型本身,而不是该类型的实例。这意味着您可以使用self消除类型属性和类型方法参数之间的歧义,就像您对实例属性和实例方法参数一样。
更一般地,您在类型方法的主体中使用的任何非限定方法和属性名称将引用其他类型级别的方法和属性。类型方法可以使用另一个方法的名称调用另一个类型方法,而无需使用类型名称作为前缀。类似地,结构和枚举上的类型方法可以通过使用不带类型名称前缀的type属性的名称来访问类型属性。