学习Swift -- 协议(上)
协议(上)
协议是Swift非常重要的部分,协议规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为遵循
这个协议。
protocol SomeProtocol {
// 协议内容
} struct SomeStruct: SomeProtocol {
// 结构体和枚举都可以遵循协议,写法以 ": 协议名"为准 多个协议名以逗号分隔
} class SomeClass { } class SubClass: SomeClass, SomeProtocol {
// 如果一个类要继承一个父类 并且遵循一个协议 写法以 ": 父类名, 协议",注意 父类名在前,协议在后
}
对属性的规定
协议可以规定其遵循者
提供特定名称和类型的实例属性
或类属性
,而不指定是存储型属性
还是计算型属性
。此外还必须指明是只读的还是可读可写的。
如果协议规定属性是可读可写的,那么这个属性不能是常量或只读的计算属性。如果协议只要求属性是只读的,那个属性不仅可以是只读的,如果你代码需要的话,也可以是可写的。
协议中的通常用var来声明属性,在类型声明后加上{ set get }
来表示属性是可读可写的,只读属性则用{ get }
来表示。
在协议中定义类属性时,总是使用static
关键字作为前缀。当协议的遵循者是类时,可以使用class
或static
关键字来声明类属性,但是在协议的定义中,仍然要使用static
关键字。
protocol SomeProtocol {
var mustBeSettable: Int { get set } // 可读可写的属性
var doesNotNeedToBeSettable: Int { get } // 只读属性
} protocol AnotherProtocol {
static var someTypeProperty: Int { get set } // 类型属性
}
下面是一个遵循FullyNamed
协议的简单结构体。
protocol FullyNamed {
var fullname: String { get }
} struct Person: FullyNamed { // 遵循了FullyNamed协议
var fullname: String // 遵循了协议,必须声明协议规定的属性 fullname
} let Alex = Person(fullname: "Alex")
下面是一个比较复杂的类
protocol FullyNamed {
var fullname: String { get }
} class Starship: FullyNamed {
var fullname: String { // 把FullyNamed的属性声明为了只读计算属性
return (prefix != nil ? prefix! + " " : "") + name
} var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
} let ncc1701 = Starship(name: "Enterprise", prefix: "USS")
ncc1701.fullname // USS Enterprise
对方法的规定
协议可以要求其遵循者实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。
正如对属性的规定中所说的,在协议中定义类方法的时候,总是使用static
关键字作为前缀。当协议的遵循者是类的时候,虽然你可以在类的实现中使用class
或者static
来实现类方法,但是在协议中声明类方法,仍然要使用static
关键字。
protocol SomeProtocol {
static func someTpyeMethod()
} protocol RandomNumberGenerator {
func random() -> Double
}
例子:
protocol RandomNumberGenerator {
func random() -> Double
} class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double { // 声明协议的方法
lastRandom = (lastRandom * a + c) % m
return lastRandom / m
}
} let generator = LinearCongruentialGenerator()
print("Here's a random number :\(generator.random())")
// 打印出 : "Here's a random number :0.37464991998171"
print("Another one: \(generator.random())")
// 打印出 : "Another one: 0.729023776863283"
对Mutating方法的规定
有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将mutating
关键字作为函数的前缀,写在func
之前,表示可以在该方法中修改它所属的实例及其实例属性的值。
注意:用类实现协议中的mutating
方法时,不用写mutating
关键字;用结构体,枚举实现协议中的mutating
方法时,必须写mutating
关键字。
protocol SomeProtocol {
mutating func mutatingFunc()
} struct SomeStruct: SomeProtocol {
var number = 2
mutating func mutatingFunc() {
number = 100
}
} class SomeClass: SomeProtocol {
var number = 1
func mutatingFunc() {
number = 100
}
}
例子:
protocol Togglable {
mutating func toggle()
} enum OnOffSwitch: Togglable {
case Off, On
mutating func toggle() {
switch self {
case On:
self = .Off
case Off:
self = .On
}
}
} var lightSwitch = OnOffSwitch.On
lightSwitch.toggle() // lightSwitch 现在为Off
对构造器的规定
协议可以要求它的遵循者实现指定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体:
protocol SomeProtocol{
init(someParameter: Int)
}
协议构造器在类中的实现
你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符。
注意:如果类已经被标记为final
,那么不需要在协议构造器的实现中使用required
修饰符。因为final类不能有子类
class SomeClass: SomeProtocol {
required init(someParameter: Int) { // 必须加required关键字
// 构造过程
}
} final class FinalClass: SomeProtocol {
init(someParameter: Int) { // 因为被标记为final,所以可以不用加required关键字
// 构造过程
}
}
protocol SomeProtocol{
init()
} class SomeSuperClass{
init() { }
} class SomeSubClass: SomeSuperClass, SomeProtocol {
// 因为遵循协议,需要加上"required", 因为继承自父类,需要加上"override"
required override init() { }
}
可失败构造器的规定
可以通过给协议Protocols
中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。
如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(init!
)。
protocol SomeProtocol{
init?()
init(someParameter: Int)
} class SomeClass: SomeProtocol{
required init() { // 或 init?() } required init!(someParameter: Int) { // 或init(someParameter: Int) }
}
协议类型
尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。
协议可以像其他普通类型一样使用,使用场景:
- 作为函数、方法或构造器中的参数类型或返回值类型
- 作为常量、变量或属性的类型
- 作为数组、字典或其他容器中的元素类型
注意:协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法。
protocol RandomNumberGenerator {
func random() -> Double
} class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c) % m)
return lastRandom / m
}
} class Dice {
let sides: Int
let generator: RandomNumberGenerator // 属性类型可以用协议类型
init(sides: Int, generator: RandomNumberGenerator){ // 参数的类型也可以是协议类型
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
} var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
d6.roll() // 3
d6.roll() // 5
d6.roll() // 4
委托(代理)模式
委托是一种设计模式,它允许类
或结构体
将一些需要它们负责的功能交由(委托)
给其他的类型的实例。委托模式的实现很简单: 定义协议来封装那些需要被委托的函数和方法, 使其遵循者
拥有这些被委托的函数和方法
。委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型信息。
protocol RandomNumberGenerator {
func random() -> Double
} class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c) % m)
return lastRandom / m
}
} class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator){
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
} protocol DiceGame {
var dice: Dice { get }
func play()
} protocol DiceGameDelegate {
func gameDidStart(game: DiceGame)
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(game: DiceGame)
} class SnakesAndLadders: DiceGame {
// 协议只要求dice为只读的,因此将dice声明为常量属性。
let dice: Dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
let finalSquare = 25
var square = 0
var board: [Int]
init() {
board = Array(count: finalSquare, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
} class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(game: DiceGame) {
// 游戏开始 做一些想做的事情
}
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
// 游戏进行中
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(game: DiceGame) {
// 游戏结束
print("The game lasted for \(numberOfTurns) turns")
}
} let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
//Rolled a 3
//Rolled a 5
//Rolled a 4
//Rolled a 5
//"The game lasted for 4 turns"
学习Swift -- 协议(上)的更多相关文章
- 学习Swift -- 协议(下)
协议(下) 在拓展中添加协议成员 通过扩展使得Dice类型遵循了一个新的协议,这和Dice类型在定义的时候声明为遵循TextRepresentable协议的效果相同.在扩展的时候,协议名称写在类型名之 ...
- TCP协议学习总结(上)
在计算机领域,数据的本质无非0和1,创造0和1的固然伟大,但真正百花齐放的还是基于0和1之上的各种层次之间的组合(数据结构)所带给我们人类各种各样的可能性.例如TCP协议,我们的生活无不无时无刻的站在 ...
- ios -- 教你如何轻松学习Swift语法(三) 完结篇
前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 一.自动引用计数 1.自动引用计数工作机制 1.1 swift和o ...
- ios -- 教你如何轻松学习Swift语法(一)
目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...
- JavaWeb学习----http协议
一.什么是HTTP协议: 1.概念: 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式(规定客户端和服务器如何 ...
- 一步一步学习Swift之(一):关于swift与开发环境配置
一.什么是Swift? 1.Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用. 2.Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制. 3.Sw ...
- 学习swift语言的快速入门教程推荐
随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...
- 開始学习swift,资料汇总帖
最近開始学习swift,以后mac和ios开发就指望它,曾经学oc半途而废了.主要原因是oc等语法实在能适应,如今有swift了.语法有js,scala,python,c++,oc等语言的影子,又一次 ...
- 协议基础:SMTP:使用Telnet学习SMTP协议
协议基础:SMTP:使用Telnet学习SMTP协议 2018-07-30 20:05:50 liumiaocn 阅读数 7479更多 分类专栏: 工具 Unix/Linux 版权声明:本文为博主 ...
随机推荐
- Excel文件上传
*&---------------------------------------------------------------------* *& FORM FRM_UPDATA_ ...
- 【转】国内较快的maven镜像
国内连接maven官方的仓库更新依赖库,网速一般很慢,收集一些国内快速的maven仓库镜像以备用. ====================国内OSChina提供的镜像,非常不错=========== ...
- jQuery练习实例(四)
最近写的jquery实例--jQuery图片九宫格样式鼠标悬停图片滑动切换效果 有兴趣的同学可以参考一下,这幅效果,个人觉得挺不错的 <%@ page language="java&q ...
- dell笔记本通过uefi+gpt模式安装win10系统
安装前,需要确认dell笔记本是否支持uefi 1.使用UltraISO制作硬盘镜像后,过程如下 1) 选择"文件"->"打开",如下 2) 在打开的对话 ...
- iOS 9 关键字的简单使用
前言: 在iOS 9 苹果推出了很多关键字, 目的其实很明确, 主要就是提高开发人员的效率, 有益于程序员之间的沟通与交流, 在开发中代码更加规范! 1. nullable 与 nonnull nul ...
- 使用SBT构建Scala项目
既然决定要在Scala上下功夫,那就要下的彻底.我们入乡随俗,学一下SBT.sbt使用ivy作为库管理工具.ivy默认把library repository建在user home下面. 安装SBT 在 ...
- get方式请求会出现中文乱码。post方式不会。
get方式请求会出现中文乱码.post方式不会. 如果是要解决get方式中文乱码问题,就需要做一个拦截器,或者在web.xml做一个get请求的配置 来自为知笔记(Wiz)
- JVM Davilk ART 简介 区别
JVM和DVM JavaSE 程序使用的虚拟机叫 Java Virtual Machine,简称 JVM.Android 平台虽然是使用java语言来开发应用程序,但Android程序却不是运行在标准 ...
- 插件GsonFormat快速实现JavaBean
转自:http://blog.csdn.net/dakaring/article/details/46300963#comments 安装方法 方法一: 1.Android studio File-& ...
- html学习的一些问题
1,什么是 W3C标准?w3c 标准不是一个标准,而是一系列标准,包括:结构标准,表现标准,动作标准. 2,内链元素和块状元素的区别内链元素允许与其他内链元素位于同一行,没有宽和高,如果想设置宽和搞, ...