Welcome-to-Swift-11方法(Methods)
方法是由特定类型关联起来的函数。类、结构体和枚举都能定义成实例方法。它封装了特定的任务和给定类型的实例的功能函数。类,结构体和方法也能定义类型方法,它只与类型的本身由关联。类型方法和Objective-C的类方法类似。
实际上,Swift与C和Objective-C的主要区别就是结构体和枚举可以定义方法。在Objective-C中,只有类才能定义方法。而在Swift中,你可以有选择性的在类、结构体或枚举中定义方法,并且使得在你创建的类中定义方法时更具有灵活性。
实例方法
实例方法是属于一个特定的类、结构体或枚举的实例的函数,函数是实例的支柱,通过提供访问实例属性和修改实例属性的方法,或过过提供相关实例功能的目的。实例方法和函数有相同的语法,就像功能的描述。
你写一个带有开和闭的实例方法作为它的所属类型,一个实例方法具有隐式访问所有其他的实例方法和属性类型。一个实例方法只能被它所属的类的特定实例调用。不能因为没有现有的实例和把孤立。
这里定义了一个简单的Counter类,它可以用来计算一个操作发生的次数:
class Counter {
var count = 0
func increment() {
count++
}
func incrementBy(amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
Counter类里定义了三个实例方法:
increment 计数器增量加1
incrementBy(amount: Int)计数器由特定的整数作为增量
reset 重置计数器的值为0
该Counter类也定义了一个属性变量count ,用来维持当前计数器的值的轨道。 你调用实例方法和属性时的语法具有相同点:
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中的方法名的第一个参数使用介词作为参考,或可以通过前面的Counter类的例子的incrementBy方法可以看出来。使用了一个可用的介词便于调用时就像在阅读句子一样方便。Swift使用这种已制定的命名方法使得在书写时更简单,调用方法时使用默认途径比使用函数参数更简洁。
具体来说,Swift在方法中默认的将第一个参数名作为方法的局部参数名,也默认的根据局部参数和外部参数名去给第二个和后续的参数命名。在你书写Objective-c方法时很熟悉这种典型的命名公约和调用约定,并使参数名称更符合你的调用方法的表达。
细想下这个替代版本的Counter类,它定义了一个更复杂的incrementBy方法:
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
这个incrementBy方法有两个参数-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相同语法风格书写,被称之为自然的表达方式。
方法的外部参数名称修饰行为
有时为方法的第一个参数提供一个外部参数名是非常有用的。尽管这不是默认的行为。你可以添加一个你自己明确的外部名称,或者你也可以使用局部名称作为外部名称并在参数名前加一个散列符号作为参数名的前缀。
相反的,如果你不想为一个方法的第二个参数或后续参数提供外部参数名,可通过使用下划线符号(_)作为该参数的显式外部参数名称来覆盖默认行为。
self 属性
每一个类型的实例都有一个称为 self 的隐式属性,它是完全等同于该实体本身的。你可以使用这个隐式的self属性饮用当前实例的实例方法。
在上面的例子中,increment方法也可以写成这样:
func increment() {
self.count++
}
在实践中,你不需要很经常的在代码中书写self,当你使用了一个已知的属性或方法名的方法时,如果你没用明确写self,Swift会假设你是指当前实例的属性或方法。Counter类里的三个使用了count(而不是self.count)实例方法就证明了这个假设。
主要的异常发生在一个实例方法的参数名和实例的属性名相同。在这种情况下,参数名优先,有必要参考属性更多的合格方式。你可以使用隐式的self属性区分参数名和属性名。
在这里,使用了self来区分名称同为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.
修改值类型的实例方法
结构体和枚举都是值类型。默认情况下,值类型的属性不能从它的内部实例方法修改。
然而,如果你需要修改你的结构体或枚举的属性在一个特定的方法中,你可以在这个方法中选择加入变异行为。然后该方法可以变异(即改变)它的属性,任何的更改在方法结束时写回原来的结构里。该方法还可以分配一个新的实例到其隐含的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)
println("The point is now at ((somePoint.x), (somePoint.y))")
// prints "The point is now at (3.0, 4.0)"
上面的Point结构体定义了一个变异的moveByX方法,它通过一定量移动一个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方法的mutating 版本创建了一个全新的结构体,并将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
}
}
}
var 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 关键字来声明。
NOTE
在Objective-C中,你只能为Objective-C类定义类型级方法。在Swift中,你可以为类、结构体和枚举定义类型级方法。每种类型方法由它的类型来明确范围的。
类型方法的调用语法和实例方法的调用方法很像。但是,你只能通过类来调用类型方法,而不是通过这个类的实例来调用。这儿有一个叫做SomeClass的类为您展示了如何调用一个类型方法:
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
在类型方法体内,隐式的self属性引用类型本身,而不是该类型的一个实例。对于 结构体和枚举,这意味着你可以使用self来消除静态你操作静态属性、静态方法参数和实例属性和实例方法参数的歧义。
更为普遍的是,你在一个类型方法体内使用任何不合格的方法和属性名称它都将引用其它类型级的方法和属性。一个类型方法可以通过其它的方法名来调用另一个类型方法,而不需要为类型名加前缀。同样,结构体和枚举的类型方法也能通过使用静态属性名访问静态属性,而不需要类型名做前缀。
下面的例子定义了一个名为LevelTracker结构体,它通过游戏的不同级别或阶段跟踪玩家的进步。这是一个单人游戏,但可以在一个设备上为多个玩家存储信息。当游戏第一次玩时所有的级别都被上锁(除了第一级)。每当一个玩家完成一个级别,该级别将对设备上的所有玩家解锁。该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结构体跟中任何玩家所解锁的最高的级别。这个值被存储在一个名为highestUnlockedLevel的静态属性里。
LevelTracker还定义了两个类型函数为highestUnlockedLevels工作。首先时一个叫unlockLevel的函数,负责当一个新的级别被解锁时去更新highestUnlockedLevel的值。第二个是一个便利的levelIsUnlocked类型函数,当一个特定的级别被解锁,它返回true。(注意,这些类型方法可以访问highestUnlockedLevel静态属性,你不需要把它写成LevelTracker.highestUnlockedLevel)
除了静态属性和类型方法,LevelTracker游戏通过一个currentLevel实例属性来追踪每个单个玩家当前正在玩的游戏级别。
为了帮助管理currentLevel属性,LevelTracker定义了一个名为advanceToLevel的实例方法。在更新currentLevel之前,该方法会检查新的游戏级别是否已经解锁。advanceToLevel方法通过返回一个bool值来指示是否能够设置currentLevel。
LevelTracker结构体被一个Player类所使用,如下所示,追踪和更新单个玩家的进度:
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的新实例用来跟踪玩家的进度。它也提供了一个名为completedLevel方法,每当玩家完成一个特定的级别时被调用。这个方法会为所有玩家解锁一个新的等级和更新玩家的进度,并将它们带入到一个新的级别里。(advanceToLevel返回的bool值将被忽略,因为在上一行LevelTracker.unlockLevel被调用时,该等级的被解锁已经是已知的了)
你可以通过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"
Welcome-to-Swift-11方法(Methods)的更多相关文章
- Swift中方法(method)所谓的lazy绑定简介
我们知道在ruby之类的动态语言中对象方法可以先从类中预先抽取,然后再应用到某个具体对象上.这称为无绑定的method对象,也可以叫做lazy绑定. 下面举个例子: irb(main):004:0&g ...
- 11 Clever Methods of Overfitting and how to avoid them
11 Clever Methods of Overfitting and how to avoid them Overfitting is the bane of Data Science in th ...
- Swift中方法闭包参数不能省略括号的一种情况
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我们知道在swift中,如果方法的最后一个参数是一个闭包类型, ...
- Swift - 初始化方法返回nil表示初始化失败
自Swift1.1开始,初始化init方法便有返回nil的能力.我们可以在init声明的时候在其后面加上一个 ? 或者 ! 来表示初始化失败时可能返回nil. 比如,给Int类添加一个将字符串初始化成 ...
- [swift]初始化方法自己主动继承
子类默认不会继承父类的初始化方法,然而,假设某种条件满足的话.父类的初始化方法还是能够继承给子类.在通常情况下,这意味着你不必复写父类的初始化方法.在安全的前提下能够以最低的代价继承父类的初始化方法. ...
- Vue.js 源码分析(五) 基础篇 方法 methods属性详解
methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head&g ...
- 初学swift笔记 方法(九)
import Foundation /* 方法 */ //实例方法 一定需要依附于对象 class MyPoint { var x: Double = 0.0 var y: Double = 0.0 ...
- Swift - 数组排序方法(附样例)
下面通过一个样例演示如何对数组元素进行排序.数组内为自定义用户对象,最终要实现按用户名排序,数据如下: 1 2 3 4 var userList = [UserInfo]() userList.app ...
- swift向方法传数组参数的语法
总是记不住向方法中传数组参数的语法,所以记录一下. func calculateStatistics(scores:[Int]) -> (min:Int,max:Int,sum:Int) { v ...
随机推荐
- COGS 1619. [HEOI2012]采花
★★☆ 输入文件:1flower.in 输出文件:1flower.out 简单对比时间限制:5 s 内存限制:128 MB [题目描述] 萧薰儿是古国的公主,平时的一大爱好是采花. 今 ...
- JavaScript内存泄露,闭包内存泄露如何解决
本文原链接:https://cloud.tencent.com/developer/article/1340979 JavaScript 内存泄露的4种方式及如何避免 简介 什么是内存泄露? Java ...
- 2018.3.16 Ubuntu 解决中文乱码问题
一.乱码的样子类似: °²Àï¿ü ÒÁ¸ñÀ³Ï£ÑÇ˹,°²Àï¿ü ÒÁ¸ñÀ³Ï£ÑÇ˹ 这种乱码称为Gedit中文乱码 打开部分Windows下的txt文本文件的时候,中文显示为乱码.但 ...
- python_88_xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单例如创建xmltest.xml文件内容如上 注:/代表自结束符号 <?xml version=&quo ...
- DROP GROUP - 删除一个用户组
SYNOPSIS DROP GROUP name DESCRIPTION 描述 DROP GROUP 从数据库中删除指定的组.组中的用户不被删除. 组中的用户不被删除. PARAMETERS 参数 n ...
- 《队长说得队》第九次团队作业:Beta冲刺与验收准备
项目 内容 这个作业属于哪个课程 >>2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 >>实验十三 团队作业9:Beta冲刺与团队项目验收 团队名 ...
- ovx openVirtex安装
搞了好久的pox实验,中途一个星期没更新了吧, 今天继续... 新手第一次搞还是在虚拟机上最安全~ ovx参照上面的做吧,注意必须在联网的情况下,否则很多控件都连不上(第一次做的时候虚拟机没配ip 坑 ...
- cocos2d-x中的基本动作
判断一个精灵被点击: 1.层要接收点击消息.2.回调函数中取得点击坐标.3.取得精灵用boudingBox().containsPoint函数判断.(或使用 convertTouchToNodeSpa ...
- SDWebImage解析
SDWebImage托管在github上.https://github.com/rs/SDWebImage 这个类库提供一个UIImageView类别以支持加载来自网络的远程图片.具有缓存管理.异步下 ...
- 如何使Recovery分区正常工作
通常安装完系统后,在进入Clover菜单选择Recovery分区后是进不去的,对于我这种完美强迫症患者来说这是不能忍的,最后,终于在网上找到个简单办法让它工作,废话不多说,上命令: 先找到Recove ...