This commit is contained in:
949478479
2015-11-09 15:22:01 +08:00
parent cf1a4a5c73
commit 8a4d9a2b36

View File

@ -18,7 +18,7 @@
**方法**是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法class methods相似。 **方法**是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法class methods相似。
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活在你创建的类型(类/结构体/枚举)上定义方法。 结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活在你创建的类型(类/结构体/枚举)上定义方法。
<a name="instance_methods"></a> <a name="instance_methods"></a>
## 实例方法 (Instance Methods) ## 实例方法 (Instance Methods)
@ -31,16 +31,16 @@
```swift ```swift
class Counter { class Counter {
var count = 0 var count = 0
func increment() { func increment() {
++count ++count
} }
func incrementBy(amount: Int) { func incrementBy(amount: Int) {
count += amount count += amount
} }
func reset() { func reset() {
count = 0 count = 0
} }
} }
``` ```
@ -54,33 +54,33 @@ class Counter {
和调用属性一样用点语法dot syntax调用实例方法 和调用属性一样用点语法dot syntax调用实例方法
```swift ```swift
let counter = Counter() let counter = Counter()
// 初始计数值是0 // 初始计数值是0
counter.increment() counter.increment()
// 计数值现在是1 // 计数值现在是1
counter.incrementBy(5) counter.incrementBy(5)
// 计数值现在是6 // 计数值现在是6
counter.reset() counter.reset()
// 计数值现在是0 // 计数值现在是0
``` ```
<a name="local_and_external_parameter"></a> <a name="local_and_external_parameter"></a>
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods) ### 方法的局部参数名称和外部参数名称 (Local and External Parameter Names for Methods)
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。 函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with``for``by`等等。前面的`Counter`类的例子中`incrementBy(_:)`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。 Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with``for``by`等等。前面的`Counter`类的例子中`incrementBy(_:)`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。
具体来说Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。 具体来说Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让富于表达性的方法在调用时不需要再限定参数名称。
看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy(_:)`方法): 看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy(_:)`方法):
```swift ```swift
class Counter { class Counter {
var count: Int = 0 var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) { func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes count += amount * numberOfTimes
} }
} }
``` ```
@ -94,17 +94,15 @@ counter.incrementBy(5, numberOfTimes: 3)
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。 你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
这种默认行为使上面代码意味着在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达的方式被调用。 上面描述的这种默认行为意味着在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然且富于表达的方式被调用。
<a name="modifying_external_parameter_name_behavior_for_methods"></a> <a name="modifying_external_parameter_name_behavior_for_methods"></a>
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods) ### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称作为第一个参数的前缀来把这个局部名称当作外部名称使用 有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你自己可以为第一个参数添加一个显式的外部名称。
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。 相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
<a name="the_self_property"></a> <a name="the_self_property"></a>
### self 属性(The self Property) ### self 属性(The self Property)
@ -114,11 +112,11 @@ counter.incrementBy(5, numberOfTimes: 3)
```swift ```swift
func increment() { func increment() {
self.count++ self.count++
} }
``` ```
实际上,你不必在你的代码里面经常写`self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确`self`Swift 假定你是指当前实例的属性或者方法。这种假定在上面的`Counter`中已经示范了:`Counter`中的三个实例方法中都使用的是`count`(而不是`self.count`)。 实际上,你不必在你的代码里面经常写`self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确`self`Swift 假定你是指当前实例的属性或者方法。这种假定在上面的`Counter`中已经示范了:`Counter`中的三个实例方法中都使用的是`count`(而不是`self.count`)。
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用`self`属性来区分参数名称和属性名称。 使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用`self`属性来区分参数名称和属性名称。
@ -126,14 +124,14 @@ func increment() {
```swift ```swift
struct Point { struct Point {
var x = 0.0, y = 0.0 var x = 0.0, y = 0.0
func isToTheRightOfX(x: Double) -> Bool { func isToTheRightOfX(x: Double) -> Bool {
return self.x > x return self.x > x
} }
} }
let somePoint = Point(x: 4.0, y: 5.0) let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOfX(1.0) { if somePoint.isToTheRightOfX(1.0) {
print("This point is to the right of the line where 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 // 打印输出: This point is to the right of the line where x == 1.0
``` ```
@ -145,17 +143,17 @@ if somePoint.isToTheRightOfX(1.0) {
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。 结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择`变异(mutating)`这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的`self`属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。 但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以选择`变异(mutating)`这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的`self`属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
要使用`变异`方法, 将关键字`mutating` 放到方法的`func`关键字之前就可以了: 要使用`变异`方法,将关键字`mutating` 放到方法的`func`关键字之前就可以了:
```swift ```swift
struct Point { struct Point {
var x = 0.0, y = 0.0 var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) { mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX x += deltaX
y += deltaY y += deltaY
} }
} }
var somePoint = Point(x: 1.0, y: 1.0) var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0) somePoint.moveByX(2.0, y: 3.0)
@ -163,14 +161,14 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印输出: "The point is now at (3.0, 4.0)" // 打印输出: "The point is now at (3.0, 4.0)"
``` ```
上面的`Point`结构体定义了一个可变方法mutating method`moveByX(_:y:)`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。 上面的`Point`结构体定义了一个可变方法mutating method`moveByX(_:y:)`用来移动点。方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,从而可以修改属性。
注意不能在结构体类型常量上调用可变方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[常量结构体的存储属性](./10_Properties.html#stored_properties_of_constant_structure_instances) 注意不能在结构体类型常量上调用可变方法,因为属性不能被改变,即使属性是变量属性,详情参见[常量结构体的存储属性](./10_Properties.html#stored_properties_of_constant_structure_instances)
```swift ```swift
let fixedPoint = Point(x: 3.0, y: 3.0) let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0) fixedPoint.moveByX(2.0, y: 3.0)
// 这里将会抛出一个错误 // 这里将会报告一个错误
``` ```
<a name="assigning_to_self_within_a_mutating_method"></a> <a name="assigning_to_self_within_a_mutating_method"></a>
@ -180,30 +178,30 @@ fixedPoint.moveByX(2.0, y: 3.0)
```swift ```swift
struct Point { struct Point {
var x = 0.0, y = 0.0 var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) { mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY) self = Point(x: x + deltaX, y: y + deltaY)
} }
} }
``` ```
新版的可变方法`moveByX(_:y:)`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。 新版的可变方法`moveByX(_:y:)`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
枚举的可变方法可以把`self`设置为相同的枚举类型中不同的成员: 枚举的可变方法可以把`self`设置为同一枚举类型中不同的成员:
```swift ```swift
enum TriStateSwitch { enum TriStateSwitch {
case Off, Low, High case Off, Low, High
mutating func next() { mutating func next() {
switch self { switch self {
case Off: case Off:
self = Low self = Low
case Low: case Low:
self = High self = High
case High: case High:
self = Off self = Off
}
} }
}
} }
var ovenLight = TriStateSwitch.Low var ovenLight = TriStateSwitch.Low
ovenLight.next() ovenLight.next()
@ -212,34 +210,34 @@ ovenLight.next()
// ovenLight 现在等于 .Off // ovenLight 现在等于 .Off
``` ```
上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off``Low``High`)之循环切换。 上面的例子中定义了一个三态开关的枚举。每次调用`next()`方法时,开关在不同的电源状态(`Off``Low``High`)之循环切换。
<a name="type_methods"></a> <a name="type_methods"></a>
## 类型方法 (Type Methods) ## 类型方法 (Type Methods)
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。类可能会用关键字`class`来允许子类重写父类的实现方法。 实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。类可能会用关键字`class`来允许子类重写父类的方法实现
> 注意 > 注意
> 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法每一个类型方法都被它所支持的类型显式包含。 > 在 Objective-C ,你只能为 Objective-C 的类定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法每一个类型方法都被它所支持的类型显式包含。
类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在`SomeClass`类上调用类型方法的例子: 类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在`SomeClass`类上调用类型方法的例子:
```swift ```swift
class SomeClass { class SomeClass {
static func someTypeMethod() { static func someTypeMethod() {
// type method implementation goes here // type method implementation goes here
} }
} }
SomeClass.someTypeMethod() SomeClass.someTypeMethod()
``` ```
在类型方法的方法体body`self`指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用`self`来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。 在类型方法的方法体body`self`指向这个类型本身,而不是类型的某个实例。这意味着你可以用`self`来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
一般来说,任何未限定的方法和属性名称,将会来自于本类中另外的类型级别的方法和属性。一个类型方法可以调用本类中另一个类型方法的名称,而无需在方法名称前面加上类型名称前缀。同样,结构体和枚举的类型方法也能够直接通过静态属性的名称访问静态属性,而不需要类型名称前缀。 一般来说,在类型方法的方法体中,任何未限定的方法和属性名称,将会指代本类中其他类型方法和类型属性。一个类型方法可以通过类型方法的名称调用本类中类型方法,而无需在方法名称前面加上类型名称前缀。同样,也能够直接通过类型属性的名称访问本类中的类型属性,而不需要类型名称前缀。
下面的例子定义了一个名为`LevelTracker`结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。 下面的例子定义了一个名为`LevelTracker`结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
游戏初始时,所有的游戏等级(除了等级 1都被锁定。每次有玩家完成一个等级这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用静态属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。 游戏初始时,所有的游戏等级(除了等级 1都被锁定。每次有玩家完成一个等级这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
```swift ```swift
struct LevelTracker { struct LevelTracker {
@ -262,11 +260,11 @@ struct LevelTracker {
} }
``` ```
`LevelTracker`监测玩家已解锁的最高等级。这个值被存储在静态属性`highestUnlockedLevel`中。 `LevelTracker`监测玩家已解锁的最高等级。这个值被存储在类型属性`highestUnlockedLevel`中。
`LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlockLevel`一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`levelIsUnlocked`如果某个给定的等级已经被解锁,它将返回`true`。(注意尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问静态属性`highestUnlockedLevel` `LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlockLevel`一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`levelIsUnlocked`如果某个给定的等级已经被解锁,它将返回`true`。(注意尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问类型属性`highestUnlockedLevel`
除了静态属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测玩家当前的等级。 除了类型属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测玩家当前的等级。
为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advanceToLevel`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advanceToLevel`方法返回布尔值以指示是否能够设置`currentLevel` 为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advanceToLevel`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advanceToLevel`方法返回布尔值以指示是否能够设置`currentLevel`
@ -274,19 +272,19 @@ struct LevelTracker {
```swift ```swift
class Player { class Player {
var tracker = LevelTracker() var tracker = LevelTracker()
let playerName: String let playerName: String
func completedLevel(level: Int) { func completedLevel(level: Int) {
LevelTracker.unlockLevel(level + 1) LevelTracker.unlockLevel(level + 1)
tracker.advanceToLevel(level + 1) tracker.advanceToLevel(level + 1)
} }
init(name: String) { init(name: String) {
playerName = name playerName = name
} }
} }
``` ```
`Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`completedLevel`方法一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。 `Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`completedLevel`方法一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。
你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么: 你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么:
@ -297,14 +295,14 @@ print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// 打印输出highest unlocked level is now 2 // 打印输出highest unlocked level is now 2
``` ```
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败: 如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败:
```swift ```swift
player = Player(name: "Beto") player = Player(name: "Beto")
if player.tracker.advanceToLevel(6) { if player.tracker.advanceToLevel(6) {
print("player is now on level 6") print("player is now on level 6")
} else { } else {
print("level 6 has not yet been unlocked") print("level 6 has not yet been unlocked")
} }
// 打印输出level 6 has not yet been unlocked // 打印输出level 6 has not yet been unlocked
``` ```