学习Swift -- 协议(下)
协议(下)
在拓展中添加协议成员
通过扩展使得Dice类型遵循了一个新的协议,这和Dice类型在定义的时候声明为遵循TextRepresentable协议的效果相同。在扩展的时候,协议名称写在类型名之后,以冒号隔开,在大括号内写明新添加的协议内容。
protocol TextRepresentable {
func asText() -> String
}
extension Dice: TextRepresentable { // 通过拓展让Dice类遵循TextRepresentable协议
func asText() -> String { // 实现协议方法
return "A \(sides)-sides dice"
}
}
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
dice.asText() // "A 6-sides dice"
通过拓展补充协议声明
当一个类型已经实现了协议中的所有要求,却没有声明为遵循该协议时,可以通过扩展(空的扩展体)来补充协议声明:
protocol TextRepresentable {
func asText() -> String
}
struct Hamster {
var name: String
// 实现了协议方法,但是没有遵循协议
func asText() -> String {
return "A hamster named: \(name)"
}
}
extension Hamster: TextRepresentable {} // 可以通过拓展是结构体遵循协议,因为实现了协议方法,所以只要写个空拓展就好。
// 通过拓展方式使Hamster遵循TextRepresentable,现在可以使用协议名作为类型
let simonTheHamster: Hamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
somethingTextRepresentable.asText() // "A hamster named: Simon"
集合中的协议类型
协议类型可以在集合中使用,表示元素都遵循了一个协议
let things: [TextRepresentable] = [simonTheHamster, somethingTextRepresentable]
for thing in things {
thing.asText()
}
协议的继承
协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:
protocol TextRepresentable {
func asText() -> String
}
protocol PrettyTextRepresentable: TextRepresentable {
func asPrettyText() -> String
}
例子
extension SnakesAndLadders: PrettyTextRepresentable {
func asText() -> String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
func asPrettyText() -> String {
var output = asText() + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "↑ "
case let snake where snake < 0:
output += "↓ "
default:
output += "○ "
}
}
return output
}
}
print(game.asPrettyText())
类专属协议
你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。
protocol SomeInheritedProtocol {
}
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 只允许类遵循的协议
}
协议合成
有时候需要同时遵循多个协议。你可以将多个协议采用protocol<SomeProtocol, AnotherProtocol>这样的格式进行组合,称为协议合成。你可以在<>中罗列任意多个你想要遵循的协议,以逗号分隔。
注意:协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
protocol Named: class {
var name: String { get }
}
protocol Aged: class {
var age: Int { get }
}
class Person: Aged, Named {
let name: String
let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let p1 = Person(name: "Alex", age: 23)
func wishHappyBirthday(celebrator: protocol<Named, Aged>) { // 参数类型规定要同时遵守两个协议的实例
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
wishHappyBirthday(p1)
// "Happy birthday Alex - you're 23!"
检查协议的一致性
你可以使用is和as操作符来检查是否遵循某一协议或强制转化为某一类型。检查和转化的语法和之前相同。
is操作符用来检查实例是否遵循了某个协议as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nilas用以强制向下转型,如果强转失败,会引起运行时错误。
protocol HasArea {
var area: Double { get }
}
class Circle: HasArea {
let pi = 3.1415926
var radius: Double
var area: Double {
return pi * radius * radius
}
init(radius: Double){
self.radius = radius
}
}
class Country: HasArea {
var area: Double
init(area: Double){
self.area = area
}
}
class Animal { // Animal没有继承HasArea
var legs: Int
init(legs: Int){
self.legs = legs
}
}
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 200_000),
Animal(legs: 4)
]
for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}
对可选协议的规定
协议可以含有可选成员,其遵循者可以选择是否实现这些成员。在协议中使用optional关键字作为前缀来定义可选成员。
可选协议在调用时使用可选链,因为协议的遵循者可能没有实现可选内容。
像someOptionalMethod?(someArgument)这样,你可以在可选方法名称后加上?来检查该方法是否被实现。可选方法和可选属性都会返回一个可选值(optional value),当其不可访问时,?之后语句不会执行,并整体返回nil
注意:可选协议只能在含有@objc前缀的协议中生效。且@objc的协议只能被类遵循这个前缀表示协议将暴露给Objective-C代码。即使你不打算和Objective-C有什么交互,如果你想要指明协议包含可选属性,那么还是要加上@obj前缀
@objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.incrementForCount?(count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
class ThreeSource: CounterDataSource {
@objc var fixedIncrement: Int = 3
}
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
// 3 6 9 12
}
class TowardsZeroSource: CounterDataSource {
@objc func incrementForCount(count: Int) -> Int {
switch count {
case 0:
return 0
case let mCount where mCount > 0:
return -1
default:
return 1
}
}
}
let counter = Counter()
counter.count = -4
counter.dataSource = TowardsZeroSource()
for _ in 1...5 {
counter.increment()
print(counter.count)
// -3 -2 -1 0 0
}
协议扩展
使用扩展协议的方式可以为遵循者提供方法或属性的实现。通过这种方式,可以让你无需在每个遵循者中都实现一次,无需使用全局函数,你可以通过扩展协议的方式进行定义。
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
}
}
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Here's a random number: 0.37464991998171
print("And here's a random Boolean: \(generator.randomBool())")
// And here's a random Boolean: true
提供默认实现
可以通过协议扩展的方式来为协议规定的属性和方法提供默认的实现。如果协议的遵循者对规定的属性和方法提供了自己的实现,那么遵循者提供的实现将被使用。
注意:通过扩展协议提供的协议实现和可选协议规定有区别。虽然协议遵循者无需自己实现,通过扩展提供的默认实现,可以不是用可选链调用。
protocol TextRepresentable {
func asText() -> String
}
protocol PrettyTextRepresentable: TextRepresentable {
func asPrettyText() -> String
var someProperty: String { get }
}
extension PrettyTextRepresentable {
func asPrettyText() -> String {
return asText() + " some text"
}
var someProperty: String {
return ""
}
}
class SomeClass: PrettyTextRepresentable {
var name = "Tom"
func asText() -> String {
return name
}
}
let person = SomeClass()
print(person.asPrettyText())
print(person.someProperty)
为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制,只有满足这些限制的协议遵循者,才能获得协议扩展提供的属性和方法。这些限制写在协议名之后,使用where关键字来描述限制情况。
protocol TextRepresentable {
func asText() -> String
}
struct Hamster: TextRepresentable {
var name: String
func asText() -> String {
return "A hamster named \(name)"
}
}
// 拓展 CollectionType 协议,当元素遵循 TextRepresentable 协议时,可用拓展中的功能
extension CollectionType where Generator.Element : TextRepresentable {
func asList() -> String {
return "some string"
}
}
let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
// 因为Array遵循CollectionType协议,数组的元素又遵循TextRepresentable协议,所以数组可以使用asList()方法得到数组内容的文本表示
print(hamsters.asList())
学习Swift -- 协议(下)的更多相关文章
- 学习Swift -- 协议(上)
协议(上) 协议是Swift非常重要的部分,协议规定了用来实现某一特定工作或者功能所必需的方法和属性.类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能.任意能够满足协议要求 ...
- 学习Swift -- 构造器(下)
构造器(下) 可失败的构造器 如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有必要的.这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某 ...
- ios -- 教你如何轻松学习Swift语法(三) 完结篇
前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 一.自动引用计数 1.自动引用计数工作机制 1.1 swift和o ...
- ios -- 教你如何轻松学习Swift语法(二)
前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 一.可选类型(重点内容) 1.什么是可选类型? 1.1在OC开 ...
- ios -- 教你如何轻松学习Swift语法(一)
目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...
- JavaWeb学习----http协议
一.什么是HTTP协议: 1.概念: 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式(规定客户端和服务器如何 ...
- 学习swift语言的快速入门教程推荐
随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...
- 协议基础:SMTP:使用Telnet学习SMTP协议
协议基础:SMTP:使用Telnet学习SMTP协议 2018-07-30 20:05:50 liumiaocn 阅读数 7479更多 分类专栏: 工具 Unix/Linux 版权声明:本文为博主 ...
- Swift-如何快速学习Swift
关于本文: 1.说明本文写作的目的 2.整理了Swift的基本语法树 3.看图作文 一.写作目的 昨天看了一个知识专栏,作者讲述的是“如何研究性的学习”.整个课程1个小时9分钟,花了我19块人民币.其 ...
随机推荐
- Spring基于注解的Cache支持
Spring为我们提供了几个注解来支持Spring Cache.其核心主要是@Cacheable和@CacheEvict.使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回 ...
- 【JAVA - SSM】之MyBatis与原生JDBC、Hibernate访问数据库的比较
首先来看一下原生JDBC访问数据库的代码: public static void main(String[] args) { // 数据库连接 Connection connection = null ...
- 【转】Android Studio Essential Training
http://ask.android-studio.org/?/explore/category-video Android Studio Essential Training内容包括:- Andro ...
- angularjs开发常见问题-1(持续更新中...)
angularJs中学习中- 1.刷新当前页面数据:$state.reload service.create(data).then(function (newItem) { flash.success ...
- Android源代码之Gallery专题研究(1)
前言 时光飞逝,从事Android系统开发已经两年了,总想写点什么来安慰自己.思考了非常久总是无法下笔,认为没什么好写的.如今最终决定写一些符合大多数人需求的东西,想必使用过Android手机的人们一 ...
- Linux模式设计系列( 内核与应用关联思考)
http://blog.chinaunix.net/uid/20608849/cid-25333-list-2.html
- Java基础知识强化之集合框架笔记60:Map集合之TreeMap(TreeMap<Student,String>)的案例
1. TreeMap(TreeMap<Student,String>)的案例 2. 案例代码: (1)Student.java: package cn.itcast_04; public ...
- linux mysql 授权以及 iptables开启3306
mysql授权ip段访问mysql grant all privileges on *.* to 'yang'@'192.168.1.%' identified by '123456'; linux ...
- FolderBrowserDialog使用
private void button_browse_Click(object sender, EventArgs e) { FolderBrowserDialog fbd = new FolderB ...
- SCIP读书笔记(1)
这书也算是必修吧,尤其是我这种非科班人员,还是应该抽时间尽量学习一下.大致翻过一遍,习题非常多,尽力吧. ##构造过程抽象 * 为了表述认知,每种语言都提供了三种机制:基本元素:组合方式:抽象方法. ...