swift 学习- 24 -- 协议 01
// 协议 定义了一个蓝图, 规定了用来实现某一特定任务或者功能的方法, 属性, 以及其他需要的东西.
// 类, 结构体, 或 枚举都可以遵循协议, 并且为协议定义的这些要求 提供具体的实现, 某个类型能够满足某个协议的要求, 就可以说该类型 遵循了这个协议
// 除了遵循协议的类型必须实现的要求外, 还可以对协议进行扩展, 通过扩展来实现一部分或者实现一些附加功能, 这样遵循的协议的类型能够使用这些功能
// 协议的语法
// 协议的定义方式 与 类, 结构体 和 枚举的定义非常相似
// protocol SomeProtocol{
// // 这里是协议的定义部分
// }
//要让自定义类型遵循某个协议, 在定义类型时, 需要在类型名称后面 加上协议名称, 中间已 (:) 分割,遵循多个协议时, 各协议之间以 (,) 分割
// struct SomeStruct: FirstProtocol,AnotherProtocol{
// // 这里是结构体的定义部分
// }
// 拥有父类的类在组训协议时, 应该将父类的类名放在协议名前面, 以 (,) 分割
// class SomeClass: SomeSuperClass.FirstProtocol,AnotherProtocol{
// // 这里是类的定义部分
// }
// 属性要求
// 协议 可以要求遵循协议的类型 提供特定名称 和 类型的 实例属性 或类型属性
// 协议 不指定属性是存储型属性 还是 计算型属性, 它只指定属性的名称 和 类型, 此外,协议号指定属性是 可读还是 可读可写的
// 如果协议要求属性是可读可写的, 那么属性不能是常量属性 或只读的计算型属性, 如果协议要求属性是可读的,那么该属性不仅是可读的, 如果代码需要的话. 还可以是可写的
// 协议总是 用 var 关键字来声明变量属性, 在类型名称后面 加上 { set get} 来表示属性是可读可写的, 可读属性则用 {get} 来声明
protocol SomeProtocol{
var mustBeSettable: Int {get set}
var doseNotNeedToBeSettable: Int {get}
}
// 在协议中定义类型属性时, 总是使用 static 关键字作为前缀, 当类类型遵循协议时, 除了 static 关键字,还可以使用 class 关键字来声明类型属性:
protocol AnotherProtocol{
static var someTypeProperty: Int {get set}
}
// 如下所示: 这是一个只含有一个实例属性要求的协议:
protocol FullNamed{
var fullName: String {get}
}
// FullNamed 协议除了要求遵循协议的类型 提供的 fullName 属性外, 并没有其他要求, 这个协议表示, 任何遵循 FullNamed 的类型, 都必须有一个刻度的 String 类型的实例属性 fullName
// 下面是一个遵循 FullNamed 协议的简单结构体
struct Person: FullNamed{
var fullName: String
}
let john = Person.init(fullName: "John Appleseed")
// 这个例子定义了一个 Person 的结构体, 用来表示一个具有名字的人, 它遵循了 FullNamed 协议
// Person 结构体的每一个实例都有一个 String 类型的存储型属性, fullName, 这正好满足了 FullNamed 协议的要求, 就以为着 Person 结构体 正确地符合了协议 (如果协议要求未被完全满足, 在编译时会报错)
// 下面是一个更复杂的例子
class Starship: FullNamed{
var prefix: String?
var name: String
init(name: String,prefix: String?) {
self.name = name
self.prefix = prefix
}
var fullName: String{
return (prefix != nil ? prefix! + " ":"") + name
}
}
var ncc1701 = Starship.init(name: "Enterprise", prefix: "USS")
// 方法要求
// 协议可以要求 遵循协议的类型 实现某些指定的实例方法 或类方法, 这些方法作为协议的一部分,像普通方法一样放在协议的定义中, 但是不需要大括号和方法体, 可以在协议中定义具有可变参数的方法, 和普通方法一样, 但是, 不支持为协议中的方法的参数提供默认值
// 正如属性要求中所述, 在协议中定义类方法的时候, 总是使用 static 关键字作为前缀, 当类类型遵循雷伊的时候,除了 static 关键字外, 还可以使用 class 关键字作为前缀.
// 下面的例子定义了一个只含有一个实例方法的协议:
protocol RandomNumberGenerator{
func random() -> Double
}
// RandomNumberGenerator 协议要求遵循协议的类型必须拥有一个名为 random ,返回值类型为 Double 的实例方法.尽管这里并未指明,但是我们假设返回值在 [0.0,1.0] 区间内
// RandomNumberGenerator 协议并不关心每一个随机数是怎么生成的,它只要求必须提供一个随机数生活器
// 如下所示:
class LinearCongrentialGenerator: RandomNumberGenerator{
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
}
let generator = LinearCongrentialGenerator.init()
print("Here's a random number : \(generator.random())")
print("And another one: \(generator.random())")
// Mutating 方法要求
// 有时需要在方法中改变方法所属的实例, 例如, 在值类型(即结构体和枚举)的实例方法中, 将 ,mutating 关键字作为方法的前缀, 写在 func 前面, 表示可以在该方法中修改它所属的实例以及实例的任意属性的值,
// 如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例, 那么在定义协议是需要在方法前加 ,mutating 关键字, 这使得结构体 和 枚举能够遵循协议并满足此方法要求
// 注意 : 实现协议中的 mutating 方法时, 若是类类型, 则不用写 mutating 关键字, 而对于结构体和枚举, 则必须写 mutataing 关键字
protocol Togglable{
mutating func toggle()
}
// toggle() 方法在定义的时候,使用 mutating 关键字标记,这表明当它被调用的时候, 该方法将会改变遵循协议的 类型的实例
// 当使用 枚举 或结构体 来实现 Togglable 协议的时候,需要提供一个带有 mutating 前缀的 toggle() 方法
enum OneOffSwitch: Togglable{
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OneOffSwitch.off
lightSwitch.toggle()
// 构造器要求
// 协议可以要求遵循协议的类型实现指定的构造器, 你可以想编写普通构造器那样, 在协议的定义里写下构造器的声明, 但不需要写花括号和构造器的实体
protocol SomeProtocol2{
init(someParameter: Int)
}
// 构造器要求在类中的实现
// 你可以在遵循协议的类中实现构造器, 无论是作为制定构造器 ,还是作为便利构造器, 无论哪种情况,你都必须为构造器实现标上 required 修饰符
class SomeClass: SomeProtocol2{
required init(someParameter: Int) {
// 这里是构造器的实现部分
}
}
// 使用 required 修饰符可以确保所有子类必须 提供此构造器实现, 从而也能复合协议
// 如果类已经被标记为 final ,那么不需要再协议的构造器的实现值使用 required 修饰符,因为 final 类 不能有子类
// 如果一个子类重写了父类的指定构造器, 并且该构造器满足了某个协议的要求, 那么该构造器的实现需要同时标注 required 和 override 修饰符
protocol SomeProtocol3 {
init()
}
class SomeSuperClass3 {
init() {
// 这里是构造器的实现部分
}
}
class SomeSubClass: SomeSuperClass3, SomeProtocol3 {
// 因为遵循协议,需要加上 required
// 因为继承自父类,需要加上 override
required override init() {
// 这里是构造器的实现部分
}
}
// 可失败构造器要求
// 协议还可以为遵循协议的类型定义可失败的构造器要求
// 遵循协议的类型可以通过课时表的构造器 (init?) 或 非可失败的构造器 (init) 来满足协议中定义的可失败的构造器的要求
// 协议中定义的非可失败构造器要求可以通过非可失败构造器 (init) 或隐式解包可失败构造器 (init!)来满足
// 协议作为类型
// 尽管协议本身并未实现任何功能, 但是协议可以被当做一个成熟的类型来使用
// 协议可以像其他类型一样使用, 使用场景如下
// 作为函数, 方法 或结构体中的参数类型 或返回值类型
// 作为常量, 变量 或属性的类型
// 作为数组, 字典 或其他容器中的元素类型
// 注意: 协议是一种类型, 因此协议类型的名称应与其他类型 (Int, Double ,String) 的写法相同, 使用大写字母开头的 驼峰式写法
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)
}
// DiceGame 协议可以被任意涉及骰子的游戏遵循, DiceGameDelegate 协议可以被任意类型遵循, 用来追踪 DiceGame 的游戏过程
// 如下所示.
class SankesAndLadders: DiceGame{
let finalSquare = 25
let dice = Dice.init(sides: 6, generator: LinearCongrentialGenerator())
var square = 0
var board: [Int]
init() {
board = [Int].init(repeating: 0, count: finalSquare + 1)
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)
}
}
// 如下实例定义了 DiceGameTracker 类, 它遵循 DiceGameDelegate 协议:
class DIcegameTracker: DiceGameDelegate{
var numberOfTurns = 0
func gameDidStart(_ game: DiceGame) {
numberOfTurns = 0
if game is SankesAndLadders {
print("Started a new game of Snaks and Ladders")
}
print("The game is using a \(game.dice.sides)-sided dice")
}
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(_ game: DiceGame) {
print("The game lasted for \(numberOfTurns) turns")
}
}
swift 学习- 24 -- 协议 01的更多相关文章
- swift 学习- 25 -- 协议 02
// 通过扩展添加协议一致性 // 即便无法修改源代码, 依然可以通过扩展 令已有类型遵循并符合协议, 扩展可以为已有类型添加属性, 方法, 下标 以及构造器, 因此可以符合协议中的相应要求 // 注 ...
- Swift学习目录
本学习基于苹果官方Swift学习材料,保留了原版90%左右的内容(一些项目开发中基本不用的知识点没有整理),并根据理解进行整理.如对原版感兴趣,可以直接单击链接阅读和学习. 第一部分 基础篇 1.基本 ...
- 协议基础:SMTP:使用Telnet学习SMTP协议
协议基础:SMTP:使用Telnet学习SMTP协议 2018-07-30 20:05:50 liumiaocn 阅读数 7479更多 分类专栏: 工具 Unix/Linux 版权声明:本文为博主 ...
- Python--网络编程学习笔记系列01 附实战:udp聊天器
Python--网络编程学习系列笔记01 网络编程基本目标: 不同的电脑上的软件能够实现数据传输 网络编程基础知识: IP地址: 用来在网络中标记一台电脑 网络号+主机号(按网络号和主机号占位分类A ...
- swift 学习笔记[1]
最近在IMOOK(网站)上自学了下swift , 总结下swift相对其他语言的不同之处 , 方便熟悉其他语言的程序员,熟悉swift语言的特性. 1. swift 的特别之处就是可以在原有的类上 , ...
- 【swift学习笔记】二.页面转跳数据回传
上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...
- 今天开始Swift学习
今天开始Swift学习 在此记录笔记 以备之后查阅! allenhuang
- iOS ---Swift学习与复习
swift中文网 http://www.swiftv.cn http://swifter.tips/ http://objccn.io/ http://www.swiftmi.com/code4swi ...
- 12套swift学习资源分享
虽然objective-c编程语言在过去很长一段时间都是iOS应用开发的基础语言,且很多iOS开发者对其也深爱有佳,但是随着swift编程语言的问世,迅速发展为开发者追捧的语言.且今年伴随着swift ...
随机推荐
- 【LeetCode】89.Gary Code
Problem: The gray code is a binary numeral system where two successive values differ in only one bit ...
- 【bzoj 3786】星系探索
Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球 ...
- oracle汉字转拼音(获得全拼/拼音首字母/拼音截取等)
oracle汉字转拼音(获得全拼/拼音首字母/拼音截取等) 效果如下: Oracle 字符集 GBK 没有问题 , UTF -8 需要修改一下 Sql代码 --oracle汉字转拼 ...
- PHPcms 缓存的读取和设置
https://blog.csdn.net/huobobo124/article/details/76912632 1.PHPcms设置了保存和读取缓存的方法,其实现方法存储在PHPcms/lib/f ...
- MongoDB初探-基本语句和数据结构
MySQL: 1 金老板 18 15512351234 2 小哪吒 20 15312341234 3 Alex 73 15512341234 MongoDB: { { id : 1, name:金老板 ...
- Coursera, Deep Learning 1, Neural Networks and Deep Learning - week1, Introduction to deep learning
整个deep learing 系列课程主要包括哪些内容 Intro to Deep learning
- 🍓 redux示例 🍓
- eureka ... is an unknown property 在 application.properties 中
问题如图 在application.properties中无法识别eureka 解决方式 (想了想这个好像是在某个依赖里面来的)发现 eureka 是在 某个依赖里面 所以添加了以下之后就解决了 ...
- mongodb系列~配置文件的优化与处理
一 简介:讲讲如何优化mongo配置文件二 常规参数 port= //端口 fork=true//守护进程方式启动mongo logpath=shard.log //mongo ...
- logistic回归为什么要使用sigmoid函数
https://www.baidu.com/link?url=LnDjrhLG7Fx6YVgR9WljUILkPZrIzOR402wr2goIS-ARtDv9TwZ2VYVbY74fyVpQlE22n ...