diff --git a/source/chapter2/11_Methods.md b/source/chapter2/11_Methods.md index 437d533c..b4c76fb4 100755 --- a/source/chapter2/11_Methods.md +++ b/source/chapter2/11_Methods.md @@ -12,44 +12,45 @@ > 翻译:[DianQK](https://github.com/DianQK),[Realank](https://github.com/Realank) 校对:[shanks](http://codebuild.me),2016-01-18 > > 2.2 -> 校对:[SketchK](https://github.com/SketchK) 2016-05-13 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-13 +> 3.0.1,shanks,2016-11-13 本页包含内容: -- [实例方法(Instance Methods)](#instance_methods) -- [类型方法(Type Methods)](#type_methods) +- [实例方法](#instance_methods) +- [类型方法](#type_methods) -**方法**是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。 +*方法*是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。 结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活地在你创建的类型(类/结构体/枚举)上定义方法。 ## 实例方法 (Instance Methods) -**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。 +*实例方法*是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。 实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。 下面的例子,定义一个很简单的`Counter`类,`Counter`能被用来对一个动作发生的次数进行计数: ```swift -class Counter { - var count = 0 - func increment() { - count += 1 - } - func incrementBy(amount: Int) { - count += amount - } - func reset() { - count = 0 - } +class Counter { + var count = 0 + func increment() { + count += 1 + } + func increment(by amount: Int) { + count += amount + } + func reset() { + count = 0 + } } ``` `Counter`类定义了三个实例方法: - `increment`让计数器按一递增; -- `incrementBy(amount: Int)`让计数器按一个指定的整数值递增; +- `increment(by: Int)`让计数器按一个指定的整数值递增; - `reset`将计数器重置为0。 `Counter`这个类还声明了一个可变属性`count`,用它来保持对当前计数器值的追踪。 @@ -61,53 +62,17 @@ let counter = Counter() // 初始计数值是0 counter.increment() // 计数值现在是1 -counter.incrementBy(5) +counter.increment(by: 5) // 计数值现在是6 counter.reset() // 计数值现在是0 ``` - -### 方法的局部参数名称和外部参数名称 (Local and External Parameter Names for Methods) - -函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。 - -Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy(_:)`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。 - -具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让富于表达性的方法在调用时不需要再限定参数名称。 - -看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy(_:)`方法): - -```swift -class Counter { - var count: Int = 0 - func incrementBy(amount: Int, numberOfTimes: Int) { - count += amount * numberOfTimes - } -} -``` - -`incrementBy(_:numberOfTimes:)`方法有两个参数: `amount`和`numberOfTimes`。默认情况下,Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法: - -```swift -let counter = Counter() -counter.incrementBy(5, numberOfTimes: 3) -// counter 的值现在是 15 -``` - -你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。 - -上面描述的这种默认行为意味着在 Swift 中,定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然且富于表达性的方式被调用。 - - -### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods) - -有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你自己可以为第一个参数添加一个显式的外部名称。 - -相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。 - +函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。 + + -### self 属性(The self Property) +### self 属性 类型的每一个实例都有一个隐含属性叫做`self`,`self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。 @@ -136,15 +101,15 @@ let somePoint = Point(x: 4.0, y: 5.0) if somePoint.isToTheRightOfX(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" ``` -如果不使用`self`前缀,Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。 - +如果不使用`self`前缀,Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。 + -### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods) +### 在实例方法中修改值类型 -结构体和枚举是**值类型**。默认情况下,值类型的属性不能在它的实例方法中被修改。 +结构体和枚举是*值类型*。默认情况下,值类型的属性不能在它的实例方法中被修改。 但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择`可变(mutating)`行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的`self`属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。 @@ -161,7 +126,7 @@ struct Point { 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)" +// 打印 "The point is now at (3.0, 4.0)" ``` 上面的`Point`结构体定义了一个可变方法 `moveByX(_:y:)` 来移动`Point`实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了`mutating`关键字,从而允许修改属性。 @@ -172,23 +137,23 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))") let fixedPoint = Point(x: 3.0, y: 3.0) fixedPoint.moveByX(2.0, y: 3.0) // 这里将会报告一个错误 -``` - +``` + -### 在可变方法中给 self 赋值(Assigning to self Within a Mutating Method) +### 在可变方法中给 self 赋值 可变方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写: ```swift -struct Point { - var x = 0.0, y = 0.0 - mutating func moveByX(deltaX: Double, y deltaY: Double) { - self = Point(x: x + deltaX, y: y + deltaY) - } +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) + } } ``` -新版的可变方法`moveByX(_:y:)`创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。 +新版的可变方法` moveBy(x:y:)`创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。 枚举的可变方法可以把`self`设置为同一枚举类型中不同的成员: @@ -216,9 +181,9 @@ ovenLight.next() 上面的例子中定义了一个三态开关的枚举。每次调用`next()`方法时,开关在不同的电源状态(`Off`,`Low`,`High`)之间循环切换。 -## 类型方法 (Type Methods) +## 类型方法 -实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做**类型方法**(Type Methods)。在方法的`func`关键字之前加上关键字`static`,来指定类型方法。类还可以用关键字`class`来允许子类重写父类的方法实现。 +实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的`func`关键字之前加上关键字`static`,来指定类型方法。类还可以用关键字`class`来允许子类重写父类的方法实现。 > 注意 > 在 Objective-C 中,你只能为 Objective-C 的类类型(classes)定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。 @@ -228,7 +193,7 @@ ovenLight.next() ```swift class SomeClass { class func someTypeMethod() { - // type method implementation goes here + // 在这里实现类型方法 } } SomeClass.someTypeMethod() @@ -243,69 +208,73 @@ SomeClass.someTypeMethod() 游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。 ```swift -struct LevelTracker { - static var highestUnlockedLevel = 1 - static func unlockLevel(level: Int) { - if level > highestUnlockedLevel { highestUnlockedLevel = level } - } - static func levelIsUnlocked(level: Int) -> Bool { - return level <= highestUnlockedLevel - } - var currentLevel = 1 - mutating func advanceToLevel(level: Int) -> Bool { - if LevelTracker.levelIsUnlocked(level) { - currentLevel = level - return true - } else { - return false - } - } +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 + } + } } ``` `LevelTracker`监测玩家已解锁的最高等级。这个值被存储在类型属性`highestUnlockedLevel`中。 -`LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlockLevel`,一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`levelIsUnlocked`,如果某个给定的等级已经被解锁,它将返回`true`。(注意,尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问类型属性`highestUnlockedLevel`) +`LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlock(_:)`,一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`isUnlocked(_:)`,如果某个给定的等级已经被解锁,它将返回`true`。(注意,尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问类型属性`highestUnlockedLevel`) 除了类型属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测每个玩家当前的等级。 -为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advanceToLevel`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advanceToLevel`方法返回布尔值以指示是否能够设置`currentLevel`。 +为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advance(to:)`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advance(to:)`方法返回布尔值以指示是否能够设置`currentLevel`。因为允许在调用`advance(to:)`时候忽略返回值,不会产生编译警告,所以函数被标注为`@ discardableResult`属性,更多关于属性信息,请参考[属性](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID347)章节。 下面,`Player`类使用`LevelTracker`来监测和更新每个玩家的发展进度: ```swift -class Player { - var tracker = LevelTracker() - let playerName: String - func completedLevel(level: Int) { - LevelTracker.unlockLevel(level + 1) - tracker.advanceToLevel(level + 1) - } - init(name: String) { - playerName = name - } +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`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`completedLevel`方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。 +`Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`complete(level:)`方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advance(to:)`返回的布尔值,因为之前调用`LevelTracker.unlock(_:)`时就知道了这个等级已经被解锁了)。 你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么: ```swift -var player = Player(name: "Argyrios") -player.completedLevel(1) +var player = Player(name: "Argyrios") +player.complete(level: 1) print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)") -// 打印输出:highest unlocked level is now 2 +// 打印 "highest unlocked level is now 2" ``` 如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败: ```swift -player = Player(name: "Beto") -if player.tracker.advanceToLevel(6) { - print("player is now on level 6") -} else { - print("level 6 has not yet been unlocked") +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 +// 打印 "level 6 has not yet been unlocked" ```