Merge pull request #90 from pp-prog/gh-pages

完成方法这章内容的翻译
This commit is contained in:
梁杰
2014-06-10 15:53:13 +08:00

View File

@ -1,16 +1,16 @@
# 方法(Methods)
**方法**是与某些特定类型相关联的功能/函数。类、结构体、枚举都可以定义实例方法;实例方法为定类型的实例封装了特定的任务与功能。类、结构体、枚举也可以定义类(型)方法(type itself)类型方法与类型自身相关联。类型方法与Objective-C中的类方法(class methods)相似。
**方法**是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法类型方法与类型自身相关联。类型方法与Objective-C中的类方法(class methods)相似。
在Swift中,结构体和枚举能够定义方法事实上这是Swift与C/Objective-C的主要区别之一。在Objective-C中,类是唯一能定义方法的类型。在Swift中你能选择是否定义一个类/结构体/枚举,并且你仍然享有在你创建的类型(类/结构体/枚举)上定义方法的灵活性
在Swift中,结构体和枚举能够定义方法事实上这是Swift与C/Objective-C的主要区别之一。在Objective-C中,类是唯一能定义方法的类型。在Swift中不仅能选择是否定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
### 实例方法(Instance Methods)
**实例方法**是某个特定类、结构体或者枚举类型的实例的方法。实例方法支撑实例的功能: 或者提供方法,以访问和修改实例属性;或者提供与实例的目的相关的功能。实例方法的语法与函数完全一致,参考[函数说明](functions.md "函数说明")。
**实例方法**是某个特定类、结构体或者枚举类型的实例的方法。实例方法支撑实例的功能: 或者提供方法,以访问和修改实例属性;或者提供与实例的目的相关的功能。实例方法的语法与函数完全一致,你可以参考[函数](../charpter2/06_Functions.md)。
实例方法要写在它所属的类型的前后括号之间。实例方法能够访问所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的特定实例调用。实例方法不能被孤立于现存的实例而被调用。
实例方法要写在它所属的类型的前后括号之间。实例方法能够隐式访问所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
下面定义一个很简单的类`Counter`的例子(`Counter`能被用来对一个动作发生的次数进行计数):
下面的例子,定义一个很简单的类`Counter``Counter`能被用来对一个动作发生的次数进行计数:
```
class Counter {
@ -27,7 +27,7 @@ class Counter {
}
```
`Counter`类定了三个实例方法:
`Counter`类定了三个实例方法:
- `increment`让计数器按一递增;
- `incrementBy(amount: Int)`让计数器按一个指定的整数值递增;
- `reset`将计数器重置为0。
@ -49,12 +49,11 @@ class Counter {
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
函数参数有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),参考[External Parameter Names](external_parameter_names.md)。对于方法参数也是这样,因为方法就是函数(只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),参考[函数的外部参数名](../06_Functions.md)。方法参数也一样(因为方法就是函数只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
Swift中的方法和Objective-C中的方法极其相似。像在Objective-C中一样Swift中方法的名称通常用一个介词指向方法的第一个参数比如`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。Swift这种方法命名约定很容易落实,因为它是用不同的默认处理方法参数的方式,而不是函数参数(来实现的)
Swift中的方法和Objective-C中的方法极其相似。像在Objective-C中一样Swift中方法的名称通常用一个介词指向方法的第一个参数比如`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。Swift这种方法命名约定让书写代码更容易了,因为Swift依靠对方法参数不同的默认处理方式,而不是函数参数来实现方法命名
具体来说Swift默认仅给方法的第一个参数名称一个局部参数名称;但是默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。
这个约定与典型的命名和调用约定相匹配这与你在写Objective-C的方法时很相似。这个约定还让expressive method调用不需要再检查/限定参数名。
具体来说Swift默认仅给方法的第一个参数名称一个局部参数名称;但是默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应这与你在写Objective-C的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
看看下面这个`Counter`的替换版本(它定义了一个更复杂的`incrementBy`方法):
@ -77,7 +76,7 @@ counter.incrementBy(5, numberOfTimes: 3)
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy`已经能很清楚地看出它的目的/作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时让他目的/作用明确。
这种默认的行为能够有效的检查方法比如你在参数numberOfTimes前写了个井号( `#` )时:
这种默认的行为能够有效的处理方法比如你在参数numberOfTimes前写了个井号( `#` )时:
```
func incrementBy(amount: Int, #numberOfTimes: Int) {
@ -87,17 +86,17 @@ func incrementBy(amount: Int, #numberOfTimes: Int) {
这种默认行为使上面代码意味着在Swift中定义方法使用了与Objective-C同样的语法风格并且方法将以自然表达式的方式被调用。
### 修改外部参数名称(Modifying External Parameter Name Behavior for Methods)
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个明确的外部名称;你也可以用一个hash符号作为第一个参数的前缀然后用这个局部名作为外部名
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称;你也可以用一个hash符号作为第一个参数的前缀这个局部名称呢更作为外部名
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,你可以通过使用下划线(`_`)作为该参数的显式外部名称来覆盖默认行为。
### `self`属性(The self Property)
类型的每一个实例都有一个隐含属性叫做`self`它完全等同于这个实力变量本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
类型的每一个实例都有一个隐含属性叫做`self``self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
上面例子中的`increment`方法可以被写成这样:
上面例子中的`increment`方法可以这样
```
func increment() {
self.count++
@ -106,11 +105,9 @@ func increment() {
实际上,你不必在你的代码里面经常写`self`。不论何时,在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写`self`Swift假定你是指当前实例的属性或者方法。这种假定在上面的`Counter`中已经示范了:`Counter`中的三个实例方法中都使用的是`count`(而不是`self.count`)
这条规则的主要例外发生在当实例方法的某个参数名称与实例的某个属性名称相同时。
在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更恰当(被限定更严格)的方式。
你可以使用隐藏的`self`属性来区分参数名称和属性名称。
这条规则的主要例外发生在当实例方法的某个参数名称与实例的某个属性名称相同时。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更恰当(被限定更严格)的方式。你可以使用`self`属性来区分参数名称和属性名称。
下面的例子演示了`self`消除方法参数`x`和实例属性`x`之间的歧义:
下面的例子中,`self`消除方法参数`x`和实例属性`x`之间的歧义:
```
struct Point {
@ -130,9 +127,9 @@ if somePoint.isToTheRightOfX(1.0) {
### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
结构体和枚举是**值类型**[Structures and Enumerations Are Value Types]("#")。一般情况下,值类型的属性不能在的实例方法中被修改。
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在的实例方法中被修改。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择`变异(mutating)`这个方法方法可以从内部变异它的属性;并且它做的任何改变在方法结束时都会回写到原始结构。方法给它隐含的`self`属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择`变异(mutating)`这个方法,然后方法可以从方法内部改变它的属性;并且它做的任何改变在方法结束时都会回写到原始结构。方法还可以给它隐含的`self`属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
`变异`方法, 将关键字`mutating` 放到方法的`func`关键字之前就可以了:
@ -150,9 +147,9 @@ println("The point is now at (\(somePoint.x), \(somePoint.y))")
// prints "The point is now at (3.0, 4.0)"
```
上面的Point结构体定义了一个变异方法(mutating method)`moveByX``moveByX`用来移动一个point。`moveByX`方法在被调用时修改了这个point,而不是返回一个新的point。方法定义加上那个了`mutating`关键字,所以方法可以修改值类型的属性了。
上面的Point结构体定义了一个变异方法(mutating method)`moveByX``moveByX`用来移动一个point。`moveByX`方法在被调用时修改了这个point,而不是返回一个新的point。方法定义加上`mutating`关键字,这才让方法可以修改值类型的属性了。
注意:不能在结构体类型的常量上调用变异方法,因为常量的属性不能被改变,就算你想改变的是常量的可变属性也不行,参考[Stored Properties of Constant Structure Instances]("#")
注意:不能在结构体类型的常量上调用变异方法,因为常量的属性不能被改变,就算你想改变的是常量的可变属性也不行,参考[存储属性和实例变量]("../10_Properties")
```
let fixedPoint = Point(x: 3.0, y: 3.0)
@ -172,9 +169,9 @@ struct Point {
}
```
新版的变异方法`moveByX`创建了一个新的分支结构(的x和y的值都被设定为目标值了)。调用这个版本的方法和调用上个版本的最终结果是一样的。
新版的变异方法`moveByX`创建了一个新的结构(的x和y的值都被设定为目标值了)。调用这个版本的方法和调用上个版本的最终结果是一样的。
枚举的变异方法可以让`self`从相同的枚举设置为不同的成员
枚举的变异方法可以让`self`从相同的枚举设置为不同的成员
```
enum TriStateSwitch {
@ -282,7 +279,7 @@ println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// prints "highest unlocked level is now 2"
```
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,你试图去设置玩家当前等级时会失败的:
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,你试图去设置玩家当前等级时会失败的:
```
player = Player(name: "Beto")
@ -293,4 +290,3 @@ println("level 6 has not yet been unlocked")
}
// prints "level 6 has not yet been unlocked"
```