Methods (方法)
Methodsare functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. Classes, structures, and enumerations can also define type methods, which are associated with the type itself. Type methods are similar to class methods in Objective-C.
方法是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。
The fact that structures and enumerations can define methods in Swift is a major difference from C and Objective-C. In Objective-C, classes are the only types that can define methods. In Swift, you can choose whether to define a class, structure, or enumeration, and still have the flexibility to define methods on the type you create.
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活地在你创建的类型(类/结构体/枚举)上定义方法。
Instance Methods (实例方法)
Instance methodsare functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose. Instance methods have exactly the same syntax as functions, as described inFunctions.
实例方法是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见函数。
You write an instance method within the opening and closing braces of the type it belongs to. An instance method has implicit access to all other instance methods and properties of that type. An instance method can be called only on a specific instance of the type it belongs to. It cannot be called in isolation without an existing instance.
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
Here’s an example that defines a simpleCounterclass, which can be used to count the number of times an action occurs:
下面的例子,定义一个很简单的Counter类,Counter能被用来对一个动作发生的次数进行计数:
class Counter {
var count=0
func increment() {
count+=1
}
func increment(byamount:Int) {
count+=amount
}
func reset() {
count=0
}
}
TheCounterclass defines three instance methods:
Counter类定义了三个实例方法:
1. increment()increments the counter by1.
increment让计数器按一递增;
2. increment(by: Int)increments the counter by a specified integer amount.
increment(by: Int)让计数器按一个指定的整数值递增;
3. reset()resets the counter to zero.
reset将计数器重置为0。
TheCounterclass also declares a variable property,count, to keep track of the current counter value.
Counter这个类还声明了一个可变属性count,用它来保持对当前计数器值的追踪。
You call instance methods with the same dot syntax as properties:
和调用属性一样,用点语法(dot syntax)调用实例方法:
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
Function parameters can have both a name (for use within the function’s body) and an argument label (for use when calling the function), as described inFunction Argument Labels and Parameter Names. The same is true for method parameters, because methods are just functions that are associated with a type.
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见指定外部参数名。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
The self Property (Self 属性)
Every instance of a type has an implicit property calledself, which is exactly equivalent to the instance itself. You use theselfproperty to refer to the current instance within its own instance methods.
类型的每一个实例都有一个隐含属性叫做self,self完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。
Theincrement()method in the example above could have been written like this:
上面例子中的increment方法还可以这样写:
func increment() {
self.count+=1
}
In practice, you don’t need to writeselfin your code very often. If you don’t explicitly writeself, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use ofcount(rather thanself.count) inside the three instance methods forCounter.
实际上,你不必在你的代码里面经常写self。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确地写self,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的Counter中已经示范了:Counter中的三个实例方法中都使用的是count(而不是self.count)。
The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use theselfproperty to distinguish between the parameter name and the property name.
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用self属性来区分参数名称和属性名称。
Here,selfdisambiguates between a method parameter calledxand an instance property that is also calledx:
下面的例子中,self消除方法参数x和实例属性x之间的歧义:
struct Point{
var x=0.0,y=0.0
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"
Without theselfprefix, Swift would assume that both uses ofxreferred to the method parameter calledx.
如果不使用self前缀,Swift 就认为两次使用的x都指的是名称为x的函数参数。
Modifying Value Types from Within Instance Methods (在实例方法中修改值类型)
Structures and enumerations arevalue types. By default, the properties of a value type cannot be modified from within its instance methods.
结构体和枚举是值类型。默认情况下,值类型的属性不能在它的实例方法中被修改。
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in tomutatingbehavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicitselfproperty, and this new instance will replace the existing one when the method ends.
但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择可变(mutating)行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的self属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
You can opt in to this behavior by placing themutatingkeyword before thefunckeyword for that method:
要使用可变方法,将关键字mutating放到方法的func关键字之前就可以了:
struct Point{
var x=0.0,y=0.0
mutating func moveBy(xdeltaX:Double,ydeltaY:Double) {
x+=deltaX
y+=deltaY
}
}
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)"
ThePointstructure above defines a mutatingmoveBy(x:y:)method, which moves aPointinstance by a certain amount. Instead of returning a new point, this method actually modifies the point on which it is called. Themutatingkeyword is added to its definition to enable it to modify its properties.
上面的Point结构体定义了一个可变方法moveByX(_:y:)来移动Point实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了mutating关键字,从而允许修改属性。
Note that you cannot call a mutating method on a constant of structure type, because its properties cannot be changed, even if they are variable properties, as described inStored Properties of Constant Structure Instances:
注意,不能在结构体类型的常量(a constant of structure type)上调用可变方法,因为其属性不能被改变,即使属性是变量属性,详情参见常量结构体的存储属性:
let fixedPoint=Point(x:3.0,y:3.0)
fixedPoint.moveBy(x:2.0,y:3.0)
// this will report an error
Assigning to self Within a Mutating Method (在可变方法中给 self 赋值)
Mutating methods can assign an entirely new instance to the implicitselfproperty. ThePointexample shown above could have been written in the following way instead:
可变方法能够赋给隐含属性self一个全新的实例。上面Point的例子可以用下面的方式改写:
struct Point{
var x=0.0,y=0.0
mutating func moveBy(xdeltaX:Double,ydeltaY:Double) {
self=Point(x:x+deltaX,y:y+deltaY)
}
}
This version of the mutatingmoveBy(x:y:)method creates a brand new structure whosexandyvalues are set to the target location. The end result of calling this alternative version of the method will be exactly the same as for calling the earlier version.
新版的可变方法moveBy(x:y:)创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。
Mutating methods for enumerations can set the implicitselfparameter to be a different case from the same enumeration:
枚举的可变方法可以把self设置为同一枚举类型中不同的成员:
enum TriStateSwitch {
case off,low,high
mutating func next() {
switchself{
case.off:
self= .low
case.low:
self= .high
case.high:
self= .off
}
}
}
varovenLight=TriStateSwitch.low
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
This example defines an enumeration for a three-state switch. The switch cycles between three different power states (off,lowandhigh) every time itsnext()method is called.
上面的例子中定义了一个三态开关的枚举。每次调用next()方法时,开关在不同的电源状态(Off,Low,High)之间循环切换。
Type Methods (类型方法)
Instance methods, as described above, are methods that are called on an instance of a particular type. You can also define methods that are called on the type itself. These kinds of methods are calledtype methods. You indicate type methods by writing thestatickeyword before the method’sfunckeyword. Classes may also use theclasskeyword to allow subclasses to override the superclass’s implementation of that method.
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做类型方法。在方法的func关键字之前加上关键字static,来指定类型方法。类还可以用关键字class来允许子类重写父类的方法实现。
NOTE
In Objective-C, you can define type-level methods only for Objective-C classes. In Swift, you can define type-level methods for all classes, structures, and enumerations. Each type method is explicitly scoped to the type it supports.
在 Objective-C 中,你只能为 Objective-C 的类类型(classes)定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。
Type methods are called with dot syntax, like instance methods. However, you call type methods on the type, not on an instance of that type. Here’s how you call a type method on a class calledSomeClass:
类型方法和实例方法一样用点语法调用。但是,你是在类型上调用这个方法,而不是在实例上调用。下面是如何在SomeClass类上调用类型方法的例子:
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
Within the body of a type method, the implicitselfproperty refers to the type itself, rather than an instance of that type. This means that you can useselfto disambiguate between type properties and type method parameters, just as you do for instance properties and instance method parameters.
在类型方法的方法体(body)中,self指向这个类型本身,而不是类型的某个实例。这意味着你可以用self来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
More generally, any unqualified method and property names that you use within the body of a type method will refer to other type-level methods and properties. A type method can call another type method with the other method’s name, without needing to prefix it with the type name. Similarly, type methods on structures and enumerations can access type properties by using the type property’s name without a type name prefix.
一般来说,在类型方法的方法体中,任何未限定的方法和属性名称,可以被本类中其他的类型方法和类型属性引用。一个类型方法可以直接通过类型方法的名称调用本类中的其它类型方法,而无需在方法名称前面加上类型名称。类似地,在结构体和枚举中,也能够直接通过类型属性的名称访问本类中的类型属性,而不需要前面加上类型名称。
The example below defines a structure calledLevelTracker, which tracks a player’s progress through the different levels or stages of a game. It is a single-player game, but can store information for multiple players on a single device.
下面的例子定义了一个名为LevelTracker结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
All of the game’s levels (apart from level one) are locked when the game is first played. Every time a player finishes a level, that level is unlocked for all players on the device. TheLevelTrackerstructure uses type properties and methods to keep track of which levels of the game have been unlocked. It also tracks the current level for an individual player.
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。LevelTracker结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
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{
returnlevel<=highestUnlockedLevel
}
@discardableResult
mutating func advance(tolevel:Int) ->Bool{
if Level Tracker.isUnlocked(level) {
currentLevel=level
returntrue
} else {
returnfalse
}
}
}
TheLevelTrackerstructure keeps track of the highest level that any player has unlocked. This value is stored in a type property calledhighestUnlockedLevel.
LevelTracker监测玩家已解锁的最高等级。这个值被存储在类型属性highestUnlockedLevel中。
LevelTrackeralso defines two type functions to work with thehighestUnlockedLevelproperty. The first is a type function calledunlock(_:), which updates the value ofhighestUnlockedLevelwhenever a new level is unlocked. The second is a convenience type function calledisUnlocked(_:), which returnstrueif a particular level number is already unlocked. (Note that these type methods can access thehighestUnlockedLeveltype property without your needing to write it asLevelTracker.highestUnlockedLevel.)
LevelTracker还定义了两个类型方法与highestUnlockedLevel配合工作。第一个类型方法是unlock(_:),一旦新等级被解锁,它会更新highestUnlockedLevel的值。第二个类型方法是isUnlocked(_:),如果某个给定的等级已经被解锁,它将返回true。(注意,尽管我们没有使用类似LevelTracker.highestUnlockedLevel的写法,这个类型方法还是能够访问类型属性highestUnlockedLevel)
In addition to its type property and type methods,LevelTrackertracks an individual player’s progress through the game. It uses an instance property calledcurrentLevelto track the level that a player is currently playing.
除了类型属性和类型方法,LevelTracker还监测每个玩家的进度。它用实例属性currentLevel来监测每个玩家当前的等级。
To help manage thecurrentLevelproperty,LevelTrackerdefines an instance method calledadvance(to:). Before updatingcurrentLevel, this method checks whether the requested new level is already unlocked. Theadvance(to:)method returns a Boolean value to indicate whether or not it was actually able to setcurrentLevel. Because it’s not necessarily a mistake for code that calls theadvance(to:)method to ignore the return value, this function is marked with the@discardableResultattribute. For more information about this attribute, seeAttributes.
为了便于管理currentLevel属性,LevelTracker定义了实例方法advance(to:)。这个方法会在更新currentLevel之前检查所请求的新等级是否已经解锁。advance(to:)方法返回布尔值以指示是否能够设置currentLevel。因为允许在调用advance(to:)时候忽略返回值,不会产生编译警告,所以函数被标注为@ discardableResult属性,更多关于属性信息,请参考属性章节。
TheLevelTrackerstructure is used with thePlayerclass, shown below, to track and update the progress of an individual player:
下面,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
}
}
ThePlayerclass creates a new instance ofLevelTrackerto track that player’s progress. It also provides a method calledcomplete(level:), which is called whenever a player completes a particular level. This method unlocks the next level for all players and updates the player’s progress to move them to the next level. (The Boolean return value ofadvance(to:)is ignored, because the level is known to have been unlocked by the call toLevelTracker.unlock(_:)on the previous line.)
Player类创建一个新的LevelTracker实例来监测这个用户的进度。它提供了complete(level:)方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了advance(to:)返回的布尔值,因为之前调用LevelTracker.unlock(_:)时就知道了这个等级已经被解锁了)。
You can create an instance of thePlayerclass for a new player, and see what happens when the player completes level one:
你还可以为一个新的玩家创建一个Player的实例,然后看这个玩家完成等级一时发生了什么:
var player=Player(name:"Argyrios")
player.complete(level:1)
print("highest unlocked level is now\(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"
If you create a second player, whom you try to move to a level that is not yet unlocked by any player in the game, the attempt to set the player’s current level fails:
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败:
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")
}
// Prints "level 6 has not yet been unlocked"