协议(上)

协议是Swift非常重要的部分,协议规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为遵循这个协议。

  1. protocol SomeProtocol {
  2. // 协议内容
  3. }
  4.  
  5. struct SomeStruct: SomeProtocol {
  6. // 结构体和枚举都可以遵循协议,写法以 ": 协议名"为准 多个协议名以逗号分隔
  7. }
  8.  
  9. class SomeClass {
  10.  
  11. }
  12.  
  13. class SubClass: SomeClass, SomeProtocol {
  14. // 如果一个类要继承一个父类 并且遵循一个协议 写法以 ": 父类名, 协议",注意 父类名在前,协议在后
  15. }

对属性的规定

协议可以规定其遵循者提供特定名称和类型的实例属性类属性,而不指定是存储型属性还是计算型属性。此外还必须指明是只读的还是可读可写的。

如果协议规定属性是可读可写的,那么这个属性不能是常量或只读的计算属性。如果协议只要求属性是只读的,那个属性不仅可以是只读的,如果你代码需要的话,也可以是可写的。

协议中的通常用var来声明属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }来表示。

在协议中定义类属性时,总是使用static关键字作为前缀。当协议的遵循者是类时,可以使用classstatic关键字来声明类属性,但是在协议的定义中,仍然要使用static关键字。

  1. protocol SomeProtocol {
  2. var mustBeSettable: Int { get set } // 可读可写的属性
  3. var doesNotNeedToBeSettable: Int { get } // 只读属性
  4. }
  5.  
  6. protocol AnotherProtocol {
  7. static var someTypeProperty: Int { get set } // 类型属性
  8. }

下面是一个遵循FullyNamed协议的简单结构体。

  1. protocol FullyNamed {
  2. var fullname: String { get }
  3. }
  4.  
  5. struct Person: FullyNamed { // 遵循了FullyNamed协议
  6. var fullname: String // 遵循了协议,必须声明协议规定的属性 fullname
  7. }
  8.  
  9. let Alex = Person(fullname: "Alex")

下面是一个比较复杂的类

  1. protocol FullyNamed {
  2. var fullname: String { get }
  3. }
  4.  
  5. class Starship: FullyNamed {
  6. var fullname: String { // 把FullyNamed的属性声明为了只读计算属性
  7. return (prefix != nil ? prefix! + " " : "") + name
  8. }
  9.  
  10. var prefix: String?
  11. var name: String
  12. init(name: String, prefix: String? = nil) {
  13. self.name = name
  14. self.prefix = prefix
  15. }
  16. }
  17.  
  18. let ncc1701 = Starship(name: "Enterprise", prefix: "USS")
  19. ncc1701.fullname // USS Enterprise

对方法的规定

协议可以要求其遵循者实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。

正如对属性的规定中所说的,在协议中定义类方法的时候,总是使用static关键字作为前缀。当协议的遵循者是类的时候,虽然你可以在类的实现中使用class或者static来实现类方法,但是在协议中声明类方法,仍然要使用static关键字。

  1. protocol SomeProtocol {
  2. static func someTpyeMethod()
  3. }
  4.  
  5. protocol RandomNumberGenerator {
  6. func random() -> Double
  7. }

例子:

  1. protocol RandomNumberGenerator {
  2. func random() -> Double
  3. }
  4.  
  5. class LinearCongruentialGenerator: RandomNumberGenerator {
  6. var lastRandom = 42.0
  7. let m = 139968.0
  8. let a = 3877.0
  9. let c = 29573.0
  10. func random() -> Double { // 声明协议的方法
  11. lastRandom = (lastRandom * a + c) % m
  12. return lastRandom / m
  13. }
  14. }
  15.  
  16. let generator = LinearCongruentialGenerator()
  17. print("Here's a random number :\(generator.random())")
  18. // 打印出 : "Here's a random number :0.37464991998171"
  19. print("Another one: \(generator.random())")
  20. // 打印出 : "Another one: 0.729023776863283"

对Mutating方法的规定

有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。

注意:用类实现协议中的mutating方法时,不用写mutating关键字;用结构体,枚举实现协议中的mutating方法时,必须写mutating关键字。

  1. protocol SomeProtocol {
  2. mutating func mutatingFunc()
  3. }
  4.  
  5. struct SomeStruct: SomeProtocol {
  6. var number = 2
  7. mutating func mutatingFunc() {
  8. number = 100
  9. }
  10. }
  11.  
  12. class SomeClass: SomeProtocol {
  13. var number = 1
  14. func mutatingFunc() {
  15. number = 100
  16. }
  17. }

例子:

  1. protocol Togglable {
  2. mutating func toggle()
  3. }
  4.  
  5. enum OnOffSwitch: Togglable {
  6. case Off, On
  7. mutating func toggle() {
  8. switch self {
  9. case On:
  10. self = .Off
  11. case Off:
  12. self = .On
  13. }
  14. }
  15. }
  16.  
  17. var lightSwitch = OnOffSwitch.On
  18. lightSwitch.toggle() // lightSwitch 现在为Off

对构造器的规定

协议可以要求它的遵循者实现指定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体:

  1. protocol SomeProtocol{
  2. init(someParameter: Int)
  3. }

协议构造器在类中的实现

你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符。

注意:如果类已经被标记为final,那么不需要在协议构造器的实现中使用required修饰符。因为final类不能有子类

  1. class SomeClass: SomeProtocol {
  2. required init(someParameter: Int) { // 必须加required关键字
  3. // 构造过程
  4. }
  5. }
  6.  
  7. final class FinalClass: SomeProtocol {
  8. init(someParameter: Int) { // 因为被标记为final,所以可以不用加required关键字
  9. // 构造过程
  10. }
  11. }
  1. protocol SomeProtocol{
  2. init()
  3. }
  4.  
  5. class SomeSuperClass{
  6. init() {
  7.  
  8. }
  9. }
  10.  
  11. class SomeSubClass: SomeSuperClass, SomeProtocol {
  12. // 因为遵循协议,需要加上"required", 因为继承自父类,需要加上"override"
  13. required override init() {
  14.  
  15. }
  16. }

可失败构造器的规定

可以通过给协议Protocols中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。

如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(init!)。

  1. protocol SomeProtocol{
  2. init?()
  3. init(someParameter: Int)
  4. }
  5.  
  6. class SomeClass: SomeProtocol{
  7. required init() { // 或 init?()
  8.  
  9. }
  10.  
  11. required init!(someParameter: Int) { // 或init(someParameter: Int)
  12.  
  13. }
  14. }

协议类型

尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。

协议可以像其他普通类型一样使用,使用场景:

  • 作为函数、方法或构造器中的参数类型或返回值类型
  • 作为常量、变量或属性的类型
  • 作为数组、字典或其他容器中的元素类型

注意:协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法。

  1. protocol RandomNumberGenerator {
  2. func random() -> Double
  3. }
  4.  
  5. class LinearCongruentialGenerator: RandomNumberGenerator {
  6. var lastRandom = 42.0
  7. let m = 139968.0
  8. let a = 3877.0
  9. let c = 29573.0
  10. func random() -> Double {
  11. lastRandom = ((lastRandom * a + c) % m)
  12. return lastRandom / m
  13. }
  14. }
  15.  
  16. class Dice {
  17. let sides: Int
  18. let generator: RandomNumberGenerator // 属性类型可以用协议类型
  19. init(sides: Int, generator: RandomNumberGenerator){ // 参数的类型也可以是协议类型
  20. self.sides = sides
  21. self.generator = generator
  22. }
  23. func roll() -> Int {
  24. return Int(generator.random() * Double(sides)) + 1
  25. }
  26. }
  27.  
  28. var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
  29. d6.roll() // 3
  30. d6.roll() // 5
  31. d6.roll() // 4

委托(代理)模式

委托是一种设计模式,它允许结构体将一些需要它们负责的功能交由(委托)给其他的类型的实例。委托模式的实现很简单: 定义协议来封装那些需要被委托的函数和方法, 使其遵循者拥有这些被委托的函数和方法。委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型信息。

  1. protocol RandomNumberGenerator {
  2. func random() -> Double
  3. }
  4.  
  5. class LinearCongruentialGenerator: RandomNumberGenerator {
  6. var lastRandom = 42.0
  7. let m = 139968.0
  8. let a = 3877.0
  9. let c = 29573.0
  10. func random() -> Double {
  11. lastRandom = ((lastRandom * a + c) % m)
  12. return lastRandom / m
  13. }
  14. }
  15.  
  16. class Dice {
  17. let sides: Int
  18. let generator: RandomNumberGenerator
  19. init(sides: Int, generator: RandomNumberGenerator){
  20. self.sides = sides
  21. self.generator = generator
  22. }
  23. func roll() -> Int {
  24. return Int(generator.random() * Double(sides)) + 1
  25. }
  26. }
  27.  
  28. protocol DiceGame {
  29. var dice: Dice { get }
  30. func play()
  31. }
  32.  
  33. protocol DiceGameDelegate {
  34. func gameDidStart(game: DiceGame)
  35. func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
  36. func gameDidEnd(game: DiceGame)
  37. }
  38.  
  39. class SnakesAndLadders: DiceGame {
  40. // 协议只要求dice为只读的,因此将dice声明为常量属性。
  41. let dice: Dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
  42. let finalSquare = 25
  43. var square = 0
  44. var board: [Int]
  45. init() {
  46. board = Array(count: finalSquare, repeatedValue: 0)
  47. board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
  48. board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
  49. }
  50. var delegate: DiceGameDelegate?
  51. func play() {
  52. square = 0
  53. delegate?.gameDidStart(self)
  54. gameLoop: while square != finalSquare {
  55. let diceRoll = dice.roll()
  56. delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
  57. switch square + diceRoll {
  58. case finalSquare:
  59. break gameLoop
  60. case let newSquare where newSquare > finalSquare:
  61. continue gameLoop
  62. default:
  63. square += diceRoll
  64. square += board[square]
  65. }
  66. }
  67. delegate?.gameDidEnd(self)
  68. }
  69. }
  70.  
  71. class DiceGameTracker: DiceGameDelegate {
  72. var numberOfTurns = 0
  73. func gameDidStart(game: DiceGame) {
  74. // 游戏开始 做一些想做的事情
  75. }
  76. func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
  77. // 游戏进行中
  78. numberOfTurns += 1
  79. print("Rolled a \(diceRoll)")
  80. }
  81. func gameDidEnd(game: DiceGame) {
  82. // 游戏结束
  83. print("The game lasted for \(numberOfTurns) turns")
  84. }
  85. }
  86.  
  87. let tracker = DiceGameTracker()
  88. let game = SnakesAndLadders()
  89. game.delegate = tracker
  90. game.play()
  91. //Rolled a 3
  92. //Rolled a 5
  93. //Rolled a 4
  94. //Rolled a 5
  95. //"The game lasted for 4 turns"

学习Swift -- 协议(上)的更多相关文章

  1. 学习Swift -- 协议(下)

    协议(下) 在拓展中添加协议成员 通过扩展使得Dice类型遵循了一个新的协议,这和Dice类型在定义的时候声明为遵循TextRepresentable协议的效果相同.在扩展的时候,协议名称写在类型名之 ...

  2. TCP协议学习总结(上)

    在计算机领域,数据的本质无非0和1,创造0和1的固然伟大,但真正百花齐放的还是基于0和1之上的各种层次之间的组合(数据结构)所带给我们人类各种各样的可能性.例如TCP协议,我们的生活无不无时无刻的站在 ...

  3. ios -- 教你如何轻松学习Swift语法(三) 完结篇

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.自动引用计数   1.自动引用计数工作机制      1.1 swift和o ...

  4. ios -- 教你如何轻松学习Swift语法(一)

    目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...

  5. JavaWeb学习----http协议

    一.什么是HTTP协议: 1.概念: 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式(规定客户端和服务器如何 ...

  6. 一步一步学习Swift之(一):关于swift与开发环境配置

    一.什么是Swift? 1.Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用. 2.Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制. 3.Sw ...

  7. 学习swift语言的快速入门教程推荐

    随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...

  8. 開始学习swift,资料汇总帖

    最近開始学习swift,以后mac和ios开发就指望它,曾经学oc半途而废了.主要原因是oc等语法实在能适应,如今有swift了.语法有js,scala,python,c++,oc等语言的影子,又一次 ...

  9. 协议基础:SMTP:使用Telnet学习SMTP协议

    协议基础:SMTP:使用Telnet学习SMTP协议 2018-07-30 20:05:50 liumiaocn 阅读数 7479更多 分类专栏: 工具 Unix/Linux   版权声明:本文为博主 ...

随机推荐

  1. DDoS deflate - Linux下防御/减轻DDOS攻击

    2010年04月19日 上午 | 作者:VPS侦探 前言 互联网如同现实社会一样充满钩心斗角,网站被DDOS也成为站长最头疼的事.在没有硬防的情况下,寻找软件代替是最直接的方法,比如用iptables ...

  2. iOS 利用Socket UDP协议广播机制的实现

    1.前言 什么是UDP协议广播机制? 举一个例. 比如在一群人群中,一个人要找张三,于是你向人群里大喊一声(广播):"谁是张三" 假设它是张三,它就会回应你.在网络中也是一样的. ...

  3. 也谈android开发图像压缩

    long long ago,给学院做的一个通讯录App需要有一个上传图像的功能,冥思苦想,绞尽脑汁后来还是没解决(学生时代的事),于是就直接上传原图了,一张图片2M到3M,这样我的应用发布之后,那绝对 ...

  4. Java基础知识强化之IO流笔记22:FileInputStream / FileOutputStream 复制文本文件案例1

    1. 使用字节流FileInputStream / FileOutputStream 复制文本文件案例: 分析: (1)数据源:从哪里来 a.txt   --   读取数据  --  FileInpu ...

  5. Linux平台的boost安装全解

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...

  6. CentOS 6.7编译安装PHP7

    1.首先配置好编译环境 yum update && yum upgrade yum groupinstall "Development Tools" yum ins ...

  7. 20个命令行工具监控 Linux 系统性能(转载)

    1. top — Linux 系统进程监控 top 命令是性能监控程序,它可以在很多 Linux/Unix 版本下使用,并且它也是 Linux 系统管理员经常使用的监控系统性能的工具.Top 命令可以 ...

  8. CI框架篇之类库篇--基础(1)

    使用 CodeIgniter 类库: 所有的类库文件存放在system/libraries 文件夹.大多数情况下你需要预先在controller中初始化后才能使用它们: $this->load- ...

  9. Context是什么,怎么用

    一.Context是什么 开始学安卓的时候发现经常有context,但是都不知道为什么,什么时候需要它. 官方文档概述:关于应用程序环境的全局信息的接口.这是一个抽象类,它的实现是由安卓系统提供的.它 ...

  10. ROW_NUMBER () 与 PARTITION组合拳

    --在一个Book表里面里有字段AuthorID与Author表关联,现在要求按PublishDate字段倒序排列,列出每个作者的前五本书.要求有没有一条语句搞定的--可用游标或者临时表--最好解决方 ...