案例代码下载

方法

方法是与特定类型相关联的函数。类,结构和枚举都可以定义实例方法,这些方法封装了用于处理给定类型的实例的特定任务和功能。类,结构和枚举也可以定义类型方法,它们与类型本身相关联。类型方法类似于Objective-C中的类方法。

事实是在Swift中结构和枚举可以定义方法是与C和Objective-C的主要区别。在Objective-C中,类是唯一可以定义方法的类型。在Swift中,可以选择是定义类,结构还是枚举,并且仍然可以灵活地在创建的类型上定义方法。

实例方法

实例方法是属于特定类,结构或枚举的实例的函数。它们通过提供访问和修改实例属性的函数,或通过提供与实例用途相关的函数。实例方法与函数具有完全相同的语法,如函数的描述。

在其所属类型的开始和结束括号内编写实例方法。实例方法可以隐式访问该类型的所有其他实例方法和属性。实例方法只能在其所属类型的特定实例上调用。如果没有现有实例,则无法单独调用它。

这是一个定义简单Counter类的示例,可用于计算操作发生的次数:

class Counter {var count = 0func increment() {count += 1}func increment(by amount: Int) {count += amount}func reset() {count = 0}
}

Counter类定义了三个实例方法:

  • increment()递增计数器1。
  • increment(by: Int) 将计数器递增指定的整数。
  • reset() 将计数器重置为零。

Counter类还声明一个变量属性count,以跟踪当前计数器值。

使用与属性相同的点语法调用实例方法:

let counter = Counter()
counter.increment()
counter.increment(by:)

函数参数既可以具有名称(在函数体内使用),也可以具有参数标签(在调用函数时使用),如函数参数标签和参数名称中所述。方法参数也是如此,因为方法只是与类型相关联的函数。

self属性

类型的每个实例都有一个名为self的隐式属性,它与实例本身完全等效。可以使用self属性在其自己的实例方法中引用当前实例。

上面示例中的increment()方法可能是这样编写的:

func increment() {self.count += 1
}

实际上,不需要经常编写self代码。如果没有显式写入self,则Swift会假定在方法中使用已知属性或方法名称时引用当前实例的属性或方法。这个假设通过在三个Counter实例方法中使用count(而不是self.count)来证明。

当实例方法的参数名称与该实例的属性具有相同的名称时,会发生此规则的主要例外。在这种情况下,参数名称优先,并且有必要以更合格的方式引用属性。可以使用该self属性来区分参数名称和属性名称。

这里,self消除了方法参数x和实例属性义x之间的歧义:

struct Point {var x = 0.0, y = 0.0func isToTheRightOf(x: Double) -> Bool {return self.x > x}
}
let point = Point(x: 4.0, y: 5.0)
if point.isToTheRightOf(x: 1.0) {print("This point is to the right of the line where x == 1.0")
}

如果没有self前缀,Swift会假设方法都引用了被调用的方法参数x。

从实例方法中修改值类型

结构和枚举是值类型。默认情况下,无法在其实例方法中修改值类型的属性。

但是,如果需要在特定方法中修改结构或枚举的属性,则可以选择改变该方法的行为。然后,该方法可以改变(即更改)其属性,并且当方法结束时,它所做的任何更改都将写回原始结构。该方法还可以为其隐式self属性分配一个全新的实例,并且该新实例将在方法结束时替换现有实例。

可以通过将mutating关键字放在该方法的func关键字之前来选择此行为:

struct Point {var x = 0.0, y = 0.0mutating func moveBy(x deltaX: Double, y deltaY: Double) {x += deltaXy += deltaY}
}
var point = Point(x: 1.0, y: 1.0)
point.moveBy(x: 2.0, y: 3.0)
print(point)

Point上面的结构定义了一个变异moveBy(x:y:)方法,它将Point实例移动一定量。此方法实际上修改了调用它的点,而不是返回一个新点。该mutating关键字被添加到它的定义,使之能够修改其属性。

请注意,不能在结构类型的常量上调用变异方法,因为它的属性不能更改,即使它们是变量属性,如常量结构实例的存储属性中所述:

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)//会报错

在变异方法中赋值给self

变异方法可以为隐式self属性分配一个全新的实例。Point上面显示的示例可能是以下列方式编写的:

struct Point {var x = 0.0, y = 0.0mutating func moveBy(x deltaX: Double, y deltaY: Double) {self = Point(x: x + deltaX, y: y + deltaY)}
}

此版本的变异moveBy(x:y:)方法创建一个新结构,其值x和y值设置为目标位置。调用该方法的替代版本的最终结果与调用早期版本完全相同。

枚举的变异方法可以将隐式self参数设置为与同一枚举不同的情况:

enum TriStateSwitch {case off, low, highmutating func next() {switch self {case .off:self = .lowcase .low:self = .highcase .high:self = .off}}
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
ovenLight.next()
print(ovenLight)

此示例定义三态切换的枚举。每次调用其next()方法时开关在三种不同的电源状态(off,low和high)之间循环。

类型方法

如上所述,实例方法是在特定类型的实例上调用的方法。还可以定义在类型本身上调用的方法。这些方法称为类型方法。通过在方法的func关键字之前写入static关键字来指示类型方法。类也可以使用class关键字来允许子类覆盖超类的该方法的实现。

注意: 在Objective-C中,只能为Objective-C类定义类型级方法。在Swift中,可以为所有类,结构和枚举定义类型级方法。每种类型方法都明确限定为它支持的类型。

使用点语法调用类型方法,如实例方法。但是,在类型上调用类型方法,而不是在该类型的实例上调用。以下是在类调用上调用类型方法SomeClass的方法:

class SomeClass {class func someTypeMethod() {print("someTypeMethod")}
}
SomeClass.someTypeMethod()

在类型方法的主体内,隐式self属性引用类型本身,而不是该类型的实例。这意味着可以使用self消除类型属性和类型方法参数之间的歧义,就像对实例属性和实例方法参数一样。

更一般地,在类型方法的主体中使用的任何非限定方法和属性名称将引用其他类型级别的方法和属性。类型方法可以使用另一个方法的名称调用另一个类型方法,而无需使用类型名称作为前缀。类似地,结构和枚举上的类型方法可以通过使用不带类型名称前缀的类型属性的名称来访问类型属性。

下面的示例定义了一个名为LevelTracker的结构,它跟踪玩家在游戏的不同级别或阶段的进度。这是一款单人游戏,但可以在一台设备上存储多个玩家的信息。

首次玩游戏时,所有游戏的等级(除了第一级)都被锁定。每当玩家完成一个等级时,该等级就会被设备上的所有玩家解锁。该LevelTracker结构使用类型属性和方法来跟踪游戏的哪些级别已解锁。它还跟踪单个玩家的当前等级。

struct LevelTracker {static var highestUnlockedLevel = 1var currentLevel = 1static func unlock(_ level: Int) {if level > highestUnlockedLevel { highestUnlockedLevel = level }}static func isUnlocked(_ level: Int) -> Bool {return level <= highestUnlockedLevel}@discardableResultmutating func advance(to level: Int) -> Bool {if LevelTracker.isUnlocked(level) {currentLevel = levelreturn true} else {return false}}
}

该LevelTracker结构跟踪任何玩家解锁的最高级别。该值存储在名为highestUnlockedLevel的类型属性中。

LevelTracker还定义了两个类型函数来处理highestUnlockedLevel属性。第一个是调用的类型函数unlock(?,它会更新highestUnlockedLevel解锁新级别时的值。第二个是调用的便捷类型函数isUnlocked(?,如果特定的级别编号已经解锁,则该函数返回true。(请注意,这些类型方法可以访问highestUnlockedLeveltype属性,而无需将其写为LevelTracker.highestUnlockedLevel。)

除了类型属性和类型方法之外,LevelTracker还可以跟踪单个玩家在游戏中的进度。它使用一个名为currentLevel的实例属性来跟踪玩家当前正在玩的等级。

要帮助管理currentLevel属性,LevelTracker定义一个名为advance(to:)的实例方法。在更新currentLevel之前,此方法检查所请求的新级别是否已解锁。advance(to:)方法返回一个布尔值,以指示它是否实际上能够设置currentLevel。因为调用advance(to:)方法忽略返回值的代码不一定是错误,所以此函数用@discardableResult属性标记。有关此属性的更多信息,请参阅属性。

LevelTracker结构与下面所示的Player类一起使用,以跟踪和更新单个玩家的进度:

class Player {var tracker = LevelTracker()let playerName: Stringfunc complete(level: Int) {LevelTracker.unlock(level + 1)tracker.advance(to: level + 1)}init(name: String) {playerName = name}
}

在Player类创建的新实例LevelTracker来跟踪玩家的进展。它还提供了一个名为complete(level:)的方法,只要玩家完成特定级别就会调用该方法。此方法为所有玩家解锁下一关,并更新玩家的进度以将其移至下一关。(advance(to:)忽略布尔返回值,因为已知通过上一行LevelTracker.unlock(_:)的调用解锁了该级别。)

可以为新玩家创建Player类的实例,并查看玩家完成第一级时会发生什么:

var player = Player(name: "Argyrios")
player.complete(level: 1)
print(LevelTracker.highestUnlockedLevel)

如你创建了第二个玩家,试图将其移动到游戏中任何玩家尚未解锁的等级,那么设置玩家当前等级的尝试将失败:

player = Player(name: "Beto")
if player.tracker.advance(to: 6) {print("player is now on level 6")
} else {print("evel 6 has not yet been unlocked")
}

Swift编程十二(方法)相关推荐

  1. Vue实现图形化积木式编程(十二)

    执行Blockly生成代码 路由 下一篇 历史回顾 Babylon.js部分 Blockly部分 前言 最终实现效果 本文内容 实现思路 问题分析 问题 原因 不优雅解决 优雅解决 完整代码 后续计划 ...

  2. Swift函数式编程十二(表格应用)

    代码地址 这个示例为希望被解析的表达式编写解析器,并为这些表达式编写一个求值器,然后将其嵌入界面中. 解析 基于解析器组合算子中的算术表达式解析器,引入额外的抽象层级. 之前,编写的解析器会直接返回计 ...

  3. swift 笔记 (十二) —— 下标

    下标 swift同意我们为 类.结构体,枚举 定义下标,以更便捷的方式訪问一大堆属性.比方Array和Dictionary都是结构体,swift的project师已经为这两个类型提供好了下标操作的代码 ...

  4. 面向对象编程(十二)——final关键字

    阅读目录 final关键字 final修饰变量 final修饰方法 final修饰类 Final && Static 总结 final关键字 Java关键字final有"这是 ...

  5. 十二个球称三次C语言编程,十二个球,有一个不知轻重,现有一个天平,称三次,找出此球!...

    平均分成A.B.C三组,每组4个: 第一秤:A.B两组先分别放天平左右: 情况一:平衡.则问题出在C组,A.B组共8个为标准球. 第二秤用3个标准球和C组的3个球对比, 如果第二秤平衡,剩下的一个就是 ...

  6. 【Swift学习】Swift编程之旅---ARC(二十)

    Swift使用自动引用计数(ARC)来跟踪并管理应用使用的内存.大部分情况下,这意味着在Swift语言中,内存管理"仍然工作",不需要自己去考虑内存管理的事情.当实例不再被使用时, ...

  7. 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条

    http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...

  8. 插入DLL和挂接API——Windows核心编程学习手札之二十二

    插入DLL和挂接API --Windows核心编程学习手札之二十二 如下情况,可能要打破进程的界限,访问另一个进程的地址空间: 1)为另一个进程创建的窗口建立子类时: 2)需要调试帮助时,如需要确定另 ...

  9. OpenCV学习笔记(二十一)——绘图函数core OpenCV学习笔记(二十二)——粒子滤波跟踪方法 OpenCV学习笔记(二十三)——OpenCV的GUI之凤凰涅槃Qt OpenCV学习笔记(二十

    OpenCV学习笔记(二十一)--绘图函数core 在图像中,我们经常想要在图像中做一些标识记号,这就需要绘图函数.OpenCV虽然没有太优秀的GUI,但在绘图方面还是做得很完整的.这里就介绍一下相关 ...

最新文章

  1. webpack chunkFilename 非入口文件的命名规则 [转]
  2. mysql-5.7.21-winx64_MySql-5.7.17 -winx64的安装配置
  3. 强制修改mysql 中root的密码
  4. jackson.ObjectMapper里enableDefaultTyping方法过期
  5. 使用管理员账户远程连接WMQ7.5的“AMQ4036”错误
  6. java mail使用qq邮箱发邮件的配置方法
  7. Knockout应用开发指南 第一章:入门
  8. bleeding edge是什么意思
  9. hbase 报:Java::JavautilConcurrent::TimeoutException:The procedure 1 is still running
  10. 自定义android tv播放器,具有可自定义实时广播源的Android TV手机播放器
  11. 数据准确性和模型准确性
  12. 天猫整站SSM项目(二)数据库设计
  13. 安卓各国语言对应缩写和时区查询
  14. matplotlib 的 spines模块详解
  15. pytorch下的lib库 源码阅读笔记(1)
  16. ps景观平面图转鸟瞰图_教你结合PS轻松绘制鸟瞰图
  17. VSCode + xUnit 编写 C# 单元测试
  18. 摄像头——环岛中拐点寻找
  19. Windows 2008 R2的备份与恢复功能
  20. 齐岳Sulfo-EGS交联剂|cas167410-92-6|乙二醇双(磺基琥珀酰亚胺基琥珀酸酯)垫片臂长/间隔长度16.1 Å

热门文章

  1. vnc连接linux服务器实现与windows文件互传
  2. 季逸超:90后IT少年的“盖茨梦”
  3. 现在流行什么邮箱?职场解读:《你好,李焕英》为什么火了?
  4. vnc server安装教程,完成vnc server的安装教程只需9步
  5. Paper intensive reading (三):Interactions Between Food and Gut Microbiota: Impact on Human Health
  6. 快手Y-Tech招聘计算机视觉工程师
  7. 快手切入蓝领招聘,58同城准备好了吗?
  8. em算法怎么对应原有分类_EM算法
  9. 使用 auto-drawing 画一个鱼骨图
  10. 2012年1月,拥有131年历史的柯达申请破产