swift方法实例

分享到:

方法是与特定类型相关联的函数。类,结构体以及枚举均可以定义实例方法,该方法为指定类型的实例封装了特定的任务与功能。类,结构体以及枚举也能定义类型方法,该方法与类型自身相关联。类型方法类似于在Objective-C中的类方法。

在Swift中,结构体和枚举能够定义方法;事实上这是Swift与C/Objective-C的主要区别之一。在Objective-C中,类是唯一能定义方法的类型。在Swift中,你可以选择是否定义一个类,结构体或枚举,且仍可以灵活地对你所创建的类型进行方法的定义。

 

实例方法

实例方法是某个特定类,结构体或枚举类型的实例的方法。他们通过提供访问的方式和修改实例属性,或提供与实例目的相关的功能性来支持这些实例的功能性。准确的来讲,实例方法的语法与函数完全一致,参考函数说明。

实例方法要写在它所属的类型的前后括号之间。实例方法能够访问他所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的特定实例调用。实例方法不能被孤立于现存的实例而被调用。

下面定义了一个简单的类Counter的示例(Counter可以用来对一个动作发生的次数进行计数):

 

class Counter {

var count = 0

func increment() {

count++

}

func incrementBy(amount: Int) {

count += amount

}

func reset() {

count = 0

 

类Counter可以定义三种实例方法:

类Counter还声明了一个变量属性、count和counter值。

和调用属性一样,用点语法调用实例方法:

 

let counter = Counter()

// the initial counter value is 0

counter.increment()

// the counter's value is now 1

counter.incrementBy(5)

// the counter's value is now 6

counter.reset()

// the counter's value is now 0

 

方法的局部参数名称和外部参数名称

函数参数有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),参考外部参数名称。对方法参数也是一样的,因为方法仅仅是与某一类型相关的函数。但是,局部名称和外部名称的默认行为不同于函数和方法。

在Swift中的方法和在Objective-C中的方法极其相似,像在Objective-C一样,在Swift中方法的名称名称通常用一个介词指向方法的第一个参数,比如:with、for以及by等等,前面的Counter类的例子中incrementBy方法就是这样的。当其被访问时,介词的使用使方法可被解读为一个句子介词的使用让方法在被调用时能像一个句子一样被解读。Swift这种方法命名约定很容易落实,因为它是用不同的默认处理方法参数的方式,而不是用函数参数(来实现的)。

具体来说,Swift默认仅给方法的第一个参数名称一个局部参数名称;但是默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相匹配,这与你在写Objective-C的方法时很相似。这个约定还让expressive method调用不需要再检查/限定参数名。 看看下面这个Counter的替换版本(它定义了一个更复杂的incrementBy方法):

 

class Counter {

var count: Int = 0

func incrementBy(amount: Int, numberOfTimes: Int) {

count += amount * numberOfTimes

}

}

 

amount和numberOfTimes。默认地,Swift仅把amount当做一个局部名称,但是把numberOfTimes既看作局部名称又看做外部名称。调用方法如下:

 

let counter = Counter()

counter.incrementBy(5, numberOfTimes: 3)

// counter value is now 15

 

你不必对第一个参数值进行外部参数名称的定义,因为从函数名incrementBy已经能很清楚地看出它的目的/作用。但是,第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时让他目的/作用明确。

这种默认的行为能够有效的检查方法,比如你在参数numberOfTimes前写了个井号(#)时:

 

func incrementBy(amount: Int, #numberOfTimes: Int) {

count += amount * numberOfTimes

}

 

这种默认行为使上面代码意味着:在Swift中定义方法使用了与Objective-C同样的语法风格,并且方法将以自然表达式的方式被调用。

修改该方法的外部参数名称

 

有时,对一个方法的第一个参数提供一个外部参数名是有用的,即使这不是默认行为。你可以自己添加一个明确的外部名称或你也可以用一个hash符号作为第一个参数的前缀,然后用这个局部名字作为外部名字。

相反,若你不想为方法的第二或后续参数提供一个外部名称,你可以通过使用下划线(_)作为该参数的显式外部名称来覆盖默认行为。

self属性

 

类型的每一实例都有一个被称为self的隐含属性,该属性完全等同于该实例本身。可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。

在上面的例子中,increment方法也可以被写成这样:

 

func increment() {

self.count++

}

 

实际上,你不必在你的代码里面经常写self。不论何时,在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写self,Swift假定你是指当前实例的属性或者方法。这种假定在上面的Counter中已经示范了:Counter中的三个实例方法中都使用的是count(而不是self.count)。

这条规则的主要例外发生在当实例方法的某个参数名称与实例的某个属性名称相同时。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更恰当(被限定更严格)的方式。你可以使用隐藏的self属性来区分参数名称和属性名称。

下面的例子演示了self消除方法参数x和实例属性x之间的歧义:

 

struct Point {

var x = 0.0, y = 0.0

func isToTheRightOfX(x: Double) -> Bool {

return self.x > x

}

}

let somePoint = Point(x: 4.0, y: 5.0)

if somePoint.isToTheRightOfX(1.0) {

println("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"

 

如果不使用self前缀,Swift就认为两次使用的x都指的是名称为x的函数参数。

在实例方法中修改值类型

 

结构体和枚举均属于值类型一般情况下,值类型的属性不能在其实例方法内被修改。

但是,如果在某个具体方法中,你需要对结构体或枚举的属性进行修改,你可以选择变异(mutating)这个方法。方法可以从内部变异它的属性;并且它做的任何改变在方法结束时都会回写到原始结构。方法会给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。

对于变异方法,将关键字mutating放到方法的func关键字之前就可以了:

 

struct Point {

var x = 0.0, y = 0.0

mutating func moveByX(deltaX: Double, y deltaY: Double) {

x += deltaX

y += deltaY

}

}

var somePoint = Point(x: 1.0, y: 1.0)

somePoint.moveByX(2.0, y: 3.0)

("The point is now at (\(somePoint.x), \(somePoint.y))")

prints "The point is now at (3.0, 4.0)"

 

上文Point结构体定义一个变异(mutating)方法moveByx,该方法按一定的数量移动Point结构体。moveByX方法在被调用时修改了这个point,而不是返回一个新的point。方法定义是加上mutating关键字,因此,方法可以修改值类型的属性。

注:不能在结构体类型的常量上调用变异方法,因为其属性不能被改变,参考常量结构体实例的存储属性:

 

let fixedPoint = Point(x: 3.0, y: 3.0)

fixedPoint.moveByX(2.0, y: 3.0)

// this will report an error

 

在变异方法中给self赋值

 

变异方法可以赋予隐含属性self一个全新的实例。上述Point的示例也可以采用下面的方式来改写:

 

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)

}

}

 

新版的变异方法moveByX创建了一个新的分支结构(他的x和y的值都被设定为目标值了)。调用这个版本的方法和调用上个版本的最终结果是一样的。 枚举的变异方法可以让self从相同的枚举设置为不同的成员:

 

enum TriStateSwitch {

case Off, Low, High

mutating func next() {

switch self {

case Off:

self = Low

case Low:

self = High

case High:

self = Off

}

ovenLight = TriStateSwitch.Low

ovenLight.next()

ovenLight is now equal to .High

ovenLight.next()

ovenLight is now equal to .Off

 

上述示例中定义了一个三态开关的枚举。每次调用next方法时,开关在不同的电源状态(Off,Low,High)之前循环切换。

类型方法

 

如上所述,实例方法是被类型的某个实例调用的方法。你也可以定义调用类型本身的方法。这种方法就叫做类型方法。声明类的类型方法,在方法的func关键字之前加上关键字class;声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static。

注:在Objective-C中,你可以定义仅适用于Objective-C的类的type-evel方法。在Swift,你可以对所有的类,结构体和枚举进行type-evel方法定义。每种类型方法仅适用于其所支持的类型。

与实例方法相同,也可利用点语法调用类型方法。但是,你需要在本类型上调用类型方法,而不是其类型实例上。下面是如何在SomeClass类上调用类型方法的示例:

 

class SomeClass {

class func someTypeMethod() {

// type method implementation goes here

}

}

SomeClass.someTypeMethod()

 

在一个类型方法的方法体内, self指向该类型本身,而不是类型的某个实例。对结构体和枚举来讲,这意味着你可以用self来消除静态属性和静态方法参数之间的二意性(类似于我们在前面处理实例属性和实例方法参数时做的那样)。

更广泛地说,你在一个类型方法主体内使用的任何未经限定的方法和属性名称将指的是其他type-level方法和属性。一种类型方法能用其他方法名称调用另一种类型方法,而无需在方法名称前面加上类型名称的前缀。同样,结构体和枚举的类型方法也能够通过使用不带有类型名称前缀的静态属性名称来访问静态属性。

下述示例中定义名为LevelTracker的结构体,该结构体通过不同级别或阶段的游戏对玩家的进度进行监测。这是一个单人游戏,但也能在单个设备上储存多个玩家的信息。

所有游戏级别(除了级别1)在游戏初始时都被锁定。每当玩家完成一个级别,在该设备上,该级别对所有的玩家解锁。LevelTracker结构体用静态属性和方法来监测解锁的游戏级别。也可监测每个玩家的当前等级。

 

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

}

 

LevelTracker结构体监测任何玩家已解锁的最高级别。该值被存储在成为hghestunockedLeve的静态属性中。

LeveTracker还定义了两个类型函数与hghestUnockedLeve配合工作。第一个为unockLevel类型函数,一旦新的级别被解锁,该函数会更新hghestUnockedLevel的值。第二个为evelIsUnocked便利型函数,若某个给定级别数已经被解锁,该函数则返回true。(注:没用使用LevelTracker.highestUnlockedLevel,这个类型方法还是能够访问静态属性highestUnlockedLevel。)

除了其静态属性和类型方法之外,LeveTracker还监测每个玩家的游戏进程。它使用currentLevel实例属性来监测玩家当前进行的级别。

为便于管理currentLevel属性,LeveTracker定义了实例方法advanceToLevel。在更新currentLevel之前,该方法检查所要求的新级别是否已经解锁。advanceToLevel方法返回布尔值以指示是否确实能够设置currentLevel了。

 

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

 

 

Player类使用LevelTracker来监测该玩家的游戏进程。它也提供competedLevel方法,一旦玩家完成某个指定等级,调用该方法。该方法为所有玩家解锁下一个级别并将当前玩家进程更新为下一个级别。(忽略了advanceToLevel布尔返回值,因为之前调用上行时就知道了这个等级已经被解锁了。)

你可以为一个新玩家创建一个Player类的实例,然后看这个玩家完成等级一时发生了什么:

 

var player = Player(name: "Argyrios")

player.completedLevel(1)

println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")

// prints "highest unlocked level is now 2"

 

如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,关于设置玩家当前等级的尝试会失败:

 

player = Player(name: "Beto")

if player.tracker.advanceToLevel(6) {

println("player is now on level 6")

} else {

println("level 6 has not yet been unlocked")

}

// prints "level 6 has not yet been unlocked"

 

昵    称:
验证码:

相关文档:

swift
IOS实例
ObjectiveC