学习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 版权声明:本文为博主 ...
随机推荐
- jQuery之事件even
jQuery之事件 W3C:http://www.w3school.com.cn/jquery/jquery_ref_events.asp 一.事件列表 1.blur() 当失去焦点时触发 ...
- [Node.js] Exporting Modules in Node
In this lesson, you will learn the difference between the exports statement and module.exports. Two ...
- [ES7] Descorator: evaluated & call order
When multiple decorators apply to a single declaration, their evaluation is similar to function comp ...
- java 字节流与字符流的区别 (转)
字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操 ...
- android开发之使用拼音搜索汉字
国庆回了趟家,昨天真不想走,离家近的感觉太好.唉,不扯这些,说说今天的正事吧. 上篇博客中介绍了自定义AutoCompleteTextView ,但是用到了一个很蹩脚的技术,就是我们事先把每个汉字的拼 ...
- 【Android】数据存储-SharedPreferences存储
简单介绍:SharedPreferences是使用键值对的方式来存储数据的,也就是说,当保存一条数据的时候,给这条数据提供一个键,如果需要读取数据,只需要通过这个键就可以提取到对应的数据. 一:存储数 ...
- Unexpected CFBundleExecutable Key
昨天晚上打包上传的时候报错: ERROR ITMS-90535: "Unexpected CFBundleExecutable Key. The bundle at 'Payload/xxx ...
- 编译安装 php 5.4.11
第一步 先下载 tzr.gz 的php源码包然后 tar zxvf php-5.4.11.tar.gz然后 cd php-5.4.11 然后复制如下编译代码 ./configure \--prefi ...
- PHP图片文件上传类型限制扩展名限制大小限制与自动检测目录创建。
程序测试网址:http://blog.z88j.com/fileuploadexample/index.html 代码分为两部分: 一部分form表单: <!doctype html> & ...
- Android studio错误及解决办法
错误: Cannot launch AVD in emulator. Output: emulator: ERROR: GPU emulation is disabled. Only screen s ...