// 通过扩展添加协议一致性

// 即便无法修改源代码, 依然可以通过扩展 令已有类型遵循并符合协议, 扩展可以为已有类型添加属性, 方法, 下标 以及构造器, 因此可以符合协议中的相应要求

// 注意: 通过扩展令已有类型遵循并符合协议时, 该类型的所有势力也会随之获得协议中定义的各项功能

protocol TextRepresentbble{

var textualDescription: String { get }

}

// 可以通过扩展, 令已有的类 Dice 遵循并符合 TextRepresentable 协议:

// extension Dice: TextOutputStream{

//     var textualDescription: String{

//         return "ddddd"

//     }

// }

// 通过扩展遵循并符合协议, 和在原始定义中遵循并符合协议的效果完全相同, 协议名称写在类型名之后, 以冒号隔开, 然后在扩展的大括号内实现协议要求的内容

// 通过扩展遵循协议

// 当一个类型已经符合某个协议中的所有要求, 却还没有声明遵循该协议时, 可以通过空扩展体来遵循该协议:

struct Hamster{

var name: String

var textualDescription: String {

return "A hamster named \(name)"

}

}

extension Hamster: TextRepresentbble{}

// 从现在起, Hamster 的实例可以作为 TextRepresentable 类型使用

let simonTheHamster = Hamster.init(name: "Simon")

let somethingTextRepresentable: TextRepresentbble = simonTheHamster

print(somethingTextRepresentable.textualDescription)

// 注意: 即使满足了协议的所有要求, 类型也不会自动遵循协议, 必须显式地遵循协议

// 协议类型的集合

// 协议类型可以在数组或字典这样的集合中使用

let things: [TextRepresentbble] = [simonTheHamster]

// 如下所示, 可以便利 things 数组, 并打印每个元素的文本信息

for thing in things {

print(thing.textualDescription)

}

// thing 是 TextRepresentbble 类型而不是 Hamster 类型, 即使实例在幕后确实是这些类型中的一种, 由于 thing 是 TextRepresentbble 类型, 任何 TextRepresentbble 的实例都有一个 textualDescription 属性, 所以每次循环中可以安全地反问 thing.textualDescription

// 协议的继承

// 协议能够继承 一个 或 多个其他协议, 可以在继承 的协议的基础上增加新的要求, 协议的继承语法与 类的继承很相似, 多个被继承的协议之间用 (,) 分割

// protocol InheritingProtocol: SomeProtocol, AnotherProtocl{

// 这里是协议的定义部分

// }

// 如下例所示:

protocol PrettyTextRepresentable: TextRepresentbble{

var prettyTextualDescription: String { get }

}

// 例子中定义了一个新的协议 PrettyTextRepresentable, 它继承自 TextRepresentable 协议, 任何遵循 PrettyTextRepresentable 洗衣的类型在满足该协议的要求时, 也必须满足 TextRepresentbble 协议的要求.

//extension SnakesAndLadders: PrettyTextRepresentable {

//    var prettyTextualDescription: String {

//        var output = textualDescription + ":\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

//    }

//}

//上述扩展令 SnakesAndLadders 遵循了 PrettyTextRepresentable 协议,并提供了协议要求的 prettyTextualDescription 属性。每个 PrettyTextRepresentable 类型同时也是 TextRepresentable 类型,所以在 prettyTextualDescription 的实现中,可以访问 textualDescription 属性。

// 类类型专属协议

// 你可以在协议的继承列表中, 通过添加 class 关键字来限制协议只能被 类类型遵循, 而结构体 或枚举不能遵循该协议, class 关键字必须第一个出现在协议的继承列表中, 在其他继承的协议之前:

// protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {

// 这里是类类型专属协议的定义部分

// }

// 在以上例子中,协议 SomeClassOnlyProtocol 只能被类类型遵循, 如果尝试让结构体或枚举 类型遵循该协议,则会导致编译错误

// 注意: 当协议定义的要求 需要遵循协议的 类型必须是 引用语义 而非 值语义, 应该采用类类型专属协议

// 协议合成

// 有时候需要同时遵循多个协议, 你可以将多个协议采用 SomeProtocol & AntherProtocol 这样的格式进行组合, 称为 协议合成. 你可以罗列任意多个你想要遵循的协议, 以与符号 (&) 分割

protocol Named{

var name: String{ get }

}

protocol Aged{

var age: Int { get }

}

struct Person: Named,Aged{

var name: String

var age: Int

}

func wishHappyBirthday(to celebrator: Named & Aged){

print("Happy birthday , \(celebrator.name), you're \(celebrator.age)")

}

let birthdayPerson = Person.init(name: "Malcolm", age: 21)

wishHappyBirthday(to: birthdayPerson)

// Named 协议包含 String 类型的 name 属性, Aged 协议包含 Int 类型的 age 属性, person 结构体遵循了这两个协议

// wishHappyBirthday(to:) 函数的参数 celebrator 的类型为 Named & Aged, 这意味着它不关心参数的具体类型, 只要参数符合这两个协议即可

// 注意: 协议合成不会生成新的, 永久的协议, 而是将多个协议中要求合成到一个只在局部作用域中有效的临时协议中

// 检查协议的一致性

// 你可以使用 类型转化 中 描述的 is 和 as 操作符来检查协议的一致性, 即是否符合某协议,并且可以指定转换到指定的协议类型, 检查很转化到某个协议类型在语法上和类型的检查和转换完全相同:

// is 用来检查视力是否符合某个协议, 若是返回 true, 否则返回 false

// as? 返回一个可选值, 当实例符合某个协议时, 返回类型为协议类型的可选值, 否则返回 nil

// as! 将实例强制向下转换到某个协议类型, 如果强制转换失败, 会引发运行时错误

protocol HasArea{

var area: Double { get }

}

class Circle: HasArea{

let pi = 3.1415927

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

}

}

// Circle 类把 area 属性实现为基于存储型属性 radius 的计算型属性, Country 类则把 area 属性实现为存储型属性, 这两个类都正确地符合了 HasArea 协议

// 下面是一个未遵循 HasArea 协议的类

class Animal{

var legs: Int

init(legs: Int) {

self.legs = legs

}

}

// Circle. Country ,Animal 并没有一个共同的基类, 尽管如此, 他们都是类, 他们的实例都可以作为 AnyObject 类型的值, 存储在同一个数组中:

let objects: [AnyObject] = [

Circle.init(radius: 2.0),

Country.init(area: 243_610),

Animal.init(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")

}

}

// 当迭代出的元素符合 HasArea 协议时, 将 as? 操作符返回的可选值通过可选绑定, 绑定到 objectWithArea 常量上, objectWithAera 是 HasArea 协议类型的实例, 因此 area 属性可以被访问和打印

// objects 数组中的元素的类型并不会因为强转而丢失类型信息, 他们仍然是 Circle , Country , Animal 类型, 然而,当他们被赋值给 objectWithArea 常量时, 只被视为 HasArea 类型, 因此只有 area 属性能被访问

// 可选的协议要求

// 协议可以定义 可选要求. 遵循协议的类型可以选择是否实现这些要求, 在协议中使用 optional 关键字作为前缀 来定义可选要求, 可选要求用在你需要和 OC 打交道的代码中, 协议和可选要求都必须带上 @objc 属性, 标记 @objc 特性的协议只能被继承自 OC 类的类 或者 @objc 类遵循, 其他类以及结构体和 枚举不能遵循这种协议

// 使用可选要求时(例如, 可选的方法或者属性) , 他们的类型会自动变成可选的, 比如, 一个类型为 (Int) -> String 的方法会变成 ((Int) -> String)?, 需要注意的是整个函数类型是可选的, 而不是返回值是可选的

// 协议中的可选要求可通过可选链式调用来使用, 因为遵循协议的类型可能没有实现这些可选要求, 类似 someOptionalMethod?(someArgument) 这样,你可以在可选方法名称后加上 ? 来调用可选方法。

@objc protocol CounterDataSource{

@objc optional func incrementForCount(count: Int) -> Int

@objc optional var fixedIncrement: Int { get }

}

// 注意: 严格来讲, CounterDataSource 协议中的方法和属性都是可选的, 因此遵循协议的类可以不实现这些要求, 尽管技术上允许这么做, 不过做好不要这么写

class Counter{

var count = 0

var dataSource: CounterDataSource?

func increment() {

if let amount = dataSource?.incrementForCount?(count: count) {

count += amount

}else if let amount = dataSource?.fixedIncrement{

count += amount

}

}

}

// 协议扩展

// 协议可以通过扩展来为遵循协议的类型提供属性, 方法以及下标的实现, 你可以基于协议本身来实现这些功能, 而无需在每个遵循协议的类型中都重复同样的实现, 也无需使用全局函数

// 例如.

//extension SomeProtocol {

//    func randomBool() -> Bool {

//        return random() > 0.5

//    }

//}

// 通过协议扩展, 所有遵循协议的类型, 都能自动获得这个扩展所增加的方法实现, 无需任何额外的修改

// 提供默认实现

// 可以通过协议扩展来为协议要求的属性, 方法以及下标 提供默认的实现, 如果遵循协议的类型为这些要求提供了自己的实现, 那么这些自定义实现将会替代扩展中的默认实现被使用

// 注意: 通过协议扩展为协议要求提供的默认实现和可选的协议要求不同, 虽然在这两种情况下, 遵循协议的类都无需自己实现这些要求, 但是通过扩展提供的默认实现可以直接调用, 而无需使用可选链式调用

extension PrettyTextRepresentable{

var prettyTextualDescription: String {

return textualDescription

}

}

// 为扩展协议添加限制条件

// 在扩展协议的时候, 可以指定一些限制条件, 只有遵循协议的类型满足这些限制条件时. 才能获得协议扩展提供的默认实现, 协议限制条件写在协议名之后, 使用 whrer 子句来描述,

// 例如,你可以扩展 CollectionType 协议,但是只适用于集合中的元素遵循了 TextRepresentable 协议的情况:

// extension CollectionType where Generator.Element: TextRepresentbble{

//     // 扩展协议定义的内容

// }

swift 学习- 25 -- 协议 02的更多相关文章

  1. swift 学习- 24 -- 协议 01

    // 协议 定义了一个蓝图, 规定了用来实现某一特定任务或者功能的方法, 属性, 以及其他需要的东西. // 类, 结构体, 或 枚举都可以遵循协议, 并且为协议定义的这些要求 提供具体的实现, 某个 ...

  2. Swift学习目录

    本学习基于苹果官方Swift学习材料,保留了原版90%左右的内容(一些项目开发中基本不用的知识点没有整理),并根据理解进行整理.如对原版感兴趣,可以直接单击链接阅读和学习. 第一部分 基础篇 1.基本 ...

  3. [转]swift 学习资源 大集合

    今天看到了一个swift的学习网站,里面收集了很多学习资源 [转自http://blog.csdn.net/sqc3375177/article/details/29206779] Swift 介绍 ...

  4. swift 学习资源 大集合

    今天看到一个swift学习网站,其中我们收集了大量的学习资源 Swift 介绍 Swift 介绍 来自 Apple 官方 Swift 简单介绍 (@peng_gong) 一篇不错的中文简单介绍 [译] ...

  5. swift学习:第一个swift程序

    原文:swift学习:第一个swift程序 最近swift有点火,赶紧跟上学习.于是,个人第一个swift程序诞生了... 新建项目

  6. swift学习 - 计时器

    swift学习之计时器 这个demo主要学习在swift中如何操作计时器(Timer),按钮(UIButton),文本(Label) 效果图: 代码 import UIKit class ViewCo ...

  7. Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml) - filter(5)

    自定义fieldType时,通常还会用到filter.filter必须跟在tokenizer或其它filter之后.如: <fieldType> <analyzer> < ...

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

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

  9. swift 学习笔记[1]

    最近在IMOOK(网站)上自学了下swift , 总结下swift相对其他语言的不同之处 , 方便熟悉其他语言的程序员,熟悉swift语言的特性. 1. swift 的特别之处就是可以在原有的类上 , ...

随机推荐

  1. PHP+MySql+Bootstrap实现用户界面数据的删除、修改与批量选择删除——实例操作

    第一步:在数据库中建立要操作的信息表 如下图: 第二步:实现对该信息表中数据的删除功能 代码如下:main(主页面) <!DOCTYPE html><html>    < ...

  2. 改变select箭头样式

    链接:https://blog.csdn.net/java_zhaoyanli/article/details/52549787 改变select箭头样式的方法: 1,去掉箭头: 2,设置图片为背景: ...

  3. VS 中NuGet 尝试还原程序包时出错"*"已拥有为"**"定义的依赖项

    之前从Git检出项目以后,项目编译不能通过,发现是缺少依赖的外部插件,于是通过NuGet去获取项目依赖的插件,如何通过NuGet恢复使用的插件请使用NuGet还原项目插件. 但是就是在使用NuGet还 ...

  4. javascript数据类型和常用内置对象(重要!)

    数据类型:w3c undefind  null  string  number  boolean  Array   object 常用内置javascript对象: Array对象:Date对象:正则 ...

  5. 什么是java字节码?

    什么是java字节码? java字码是java源程序代码的一种较为低级的表示.Java编译器将源代码编译成字码后,就可以Java解释器执行

  6. TCP通信实现对接硬件发送与接收十六进制数据 & int与byte的转换原理 & java中正负数的表示

    今天收到的一份需求任务是对接硬件,TCP通信,并给出通信端口与数据包格式,如下: 1.首先编写了一个简单的十六进制转byte[]数组与byte[]转换16进制字符串的两个方法,如下: /** * 将十 ...

  7. Lock类-ReentrantLock的使用

    在Java多线程中可以使用synchronized隐式锁实现线程之间同步互斥,Java5中提供了Lock类(显示锁)也可以实现线程间的同步,而且在使用上更加方便.本文主要研究 ReentrantLoc ...

  8. Hadoop Streaming开发要点

    一.shell脚本中的相关配置 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop" STREAM_JAR_PATH=" ...

  9. Latex 问题解决

    1. 当bib文件中包含待引用的参考文献,并且在tex中正常通过\cite{}引用,却依然提示citation undefined,pdf中显示问号时,怎么解决报错. 解决:删除根目录下的  .bbl ...

  10. FlowNet2.0 安装指南

     \(安装环境: \color{red}{Ubuntu16.04 + CUDA8.0 + cuDNN5.0}\) 安装 CUDA CUDA 安装准备 CUDA 官方安装文档 首先查看是否电脑具有支持 ...