Swift4 协议
创建: 2018/02/27
完成: 2018/02/28
更新: 2018/03/07 增加类采用协议时的注意
补充只有类, 结构体, 枚举型可以采用协议
增加为类定义准备的协议( protocol Sample: class, ... { ... } )
【任务表】TODO
协议(protocol) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议 |
区分于具体的实现, 集中进行类型应该有的方法和属性的声明的功能 ● 应用某协议叫做采用协议 ● 只有类, 结构体, 枚举型可以采用协议
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
作为程序构成单位的协议 |
● 其他语言里叫协议(protocol)/接口(interface)/抽象类 ● 协议, 扩张, 泛型紧密相关 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
采用协议 |
采用协议的写法: struct/class 类型名: 协议名 { // 多个协议用逗号隔开 ... } 例 public protocol CustomStringConvertiable { // 固有的协议, 采用的可以直接作为文字参数, 如放到print里 public var description: String { get } } struct Sample: CustomStringConvertiable { // 采用上面协议的结构体 ... } ● 可被构造体, 类采用
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议的声明 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议声明的概要 |
protocol 协议名: 继承的协议 { // 可以不继承 static func 方法(参数) -> 类型 // 实例方法,静态方法 static var 属性名: 类型 { get set } // 实例属性, 静态属性: set可省略 static 运算符种类 func 运算符(参数) -> 类型 // 运算符: 二项运算符不用写种类 init(参数) subscript(参数) -> 类型 { get set } // 索引: set可省略 typealias 新型名 = 原型名 associatedtype 识别符号 } ● 顺序不限,数量不限 ● 协议可以继承其他协议 协议里可以包含 ● 实例方法, 静态方法 ● 实例属性, 静态属性 ● 运算符的函数声明 ● 构造函数 ● 索引(subscript) ● 附属型(typealias, associatedtype), 不能在协议里自己定义新的型 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
类采用协议的注意 |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
为类定义准备的协议 |
● 只有类, 结构体, 枚举型可以采用协议 ● 只运行类采用的协议定义方法 protocol 协议名: class, 继承的协议 { // 协议名后加上class ... // 方法不用也不能附mutating }
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
方法的声明与定义 |
● 改变自身的方法前面加mutating(类协议不需要) ● 只记声明,不写定义。可以在扩展里写定义作为默认的实现 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
属性的声明与定义 |
● 指定可读或者读写, get和set顺序不限, 只写声明 ● 指定为可读的可以实现为读写 ● 协议里都要写作var ● 计算型和容纳型都可以, 不限定 ● 静态属性前面加static |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
索引的声明与定义 |
和属性的声明与定义一样 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
作为型的协议 |
func 方法名(参数1: [协议], ...) -> 类型 {
...
}
● 只能执行协议声明的操作 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议的继承 |
protocol 协议: 继承的协议 {
...
}
● 逗号隔开 ● 实现方法自由 ● 不能有重名不同型的属性 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议的合成 |
协议1 & 协议2 ... // 几个已有协议的和集合, 不添加任何新东西 //相当于 protocol newProtocol: 协议1, 协议2, ...
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议的例 | # TODO: Supply [使用协议的例子] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
协议与附属型 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
附属型的声明 |
associatedtype 附属型名 = 默认的型 ● 可以设定默认的型, 没有声明或者无法推导时使用自动判断为该型 ● 构造体, 类, 枚举型内部可以定义新型 ● 属于泛型, 型参数 ● 编译时候静态解析并替换为具体的型 ● 决定具体型的方法 // 以此协议为例 protocol TestProtocol { associatedtype SampleData //附属型SampleData var data: SampleData { get set } } (a)用typealias给出具体型 struct Sample1: TestProtocol { typealias SampleData = Int // typealias来决定具体型 ... } (b)定义型(内部嵌套的型)来给出定义 struct Sample2: TestProtocol { struct SampleData { var x: Int, y: Int } // 嵌套型来给出定义, 也就是在内部定义一个和附属型名字一样的型 ... } (c)从型的使用方法来推导出型 struct Sample3: TestProtocol { var data: Double // 根据型推导。因为协议里有var data: SampleData { get set } ... }
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Self |
● 表示采用该协议的型 ● 只能在协议和类中使用 在类中只能作为方法的返回值 ● 附属型的表达方法 Self.附属型名 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
向类型参数添加限制 |
● associatedtype A 没有任何限制 ● associatedtype A: 协议名 类型A必须采用右侧指定的协议, 多个用逗号,隔开 associatedtype SampleData: Equatable ● associatedtype A: 协议名 where 条件 多个协议的话where放在最后 类型A必须采用协议, 并且必须满足指定条件(多个条件用,隔开) 条件为 (1) 型: 协议 指定的型必须采用右侧协议 associatedtype SampleData: CustomStringConvertible where SampleData: Equatable
(2) 型1 == 型2 型1, 型2同型 associatedtype SampleData: CustomStringConvertible where SampleData == String
(3) 另外继承协议的话, 可以对其他协议的附属型做上述限制 protocol SampleProtocol: SampleParentProtocol, Equatable { associatedtype SampleData: Equatable where Self.ParentSampleData == String ... }
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
主要的协议 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
相等 Equatable |
public protocol Equatable { static func ==(lhs:Self, rhs: Self) -> Bool }
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
大小关系 Comparable |
public protocol Comparable: Equatable { public static func <(lhs: Self, rhs: Self) -> Bool public static func <=(lhs: Self, rhs: Self) -> Bool public static func >=(lhs: Self, rhs: Self) -> Bool public static func >(lhs: Self, rhs: Self) -> Bool } ● 只需要实现==和< ● 采用Comparable的可以使用sorted()来排序
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
表示序列 Sequence |
可以迭代的型(数组, 哈希表, 字符串等) Sequence协议定义的主要部分 public protocol Sequence { associatedtype Element // 元素类型 associatedtype Iterator: IteratorProtocol where Iterator.Element == Element // 迭代器 associatedtype SubSequence // 子列型 func makeIterator() -> Iterator // 生成迭代器 func map<T>(_: (Element) throws -> T) rethrows -> [T] func filter(_: (Element) throws -> Bool) rethrows -> [Element] func forEach(_ body: (Element) throw -> Void) rethrows func dropFirst(_ n: Int) -> SubSequence // 去除开头 func dropLast(_ n: Int) -> SubSequence //去除末尾 func prefix(_ maxLength: Int) -> SubSequence // 开头开始的部分列 func suffix(_ maxLength: Int) -> SubSequence // 末尾部分的部分列 } IteratorProtocol public protocol IteratorProtocol { associatedtype Element mutating func next() -> Self.Element? // 返回一个要素,没了返回nil } ● 只要自定义makeIterator()就可以采用 # TODO: Supply [创建采用Sequence的类型] ● 主要方法(数组, 哈希表, 字符串等都可以用), 不含带闭包的方法 # TODO: Supply [补充带闭包的参数 13.4] T: 元素的类型(Element), S: 采用Sequence的类型
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
可以用索引来获取元素 Collection |
可以用索引来获取元素的型(数组, 哈希表, 字符串) ● 继承Sequence ● Collection协议定义的主要部分 public protocol Collection: Sequence { associatedtype Index: Comparable //索引类型 var startIndex: Index { get } // 开头的索引 var endIndex: Index { get } // 末尾的下一个的索引 associatedtype Element // 元素类型 associatedtype IndexDisrance = Int // 索引之间的差 associatedtype Iterator = IndexingIterator<Self> // 迭代器的型 func makeIterator() -> Iterator // 生成迭代器 associatedtype SubSequence: Sequence = Slice<Self> where SubSequence.SubSequence == SubSequence, Element == SubSequence.Element, SubSequence.Index == Index subscript(position: Index) -> Element { get } // 获取指定索引处的元素 subscript(bounds: Range<Index>) -> SubSequence {get} // 获取指定返回的元素序列 associated Indices: Sequence = DefaultIndices<Self> where Indices.Element == Element, Indices.Index == index, Indices.SubSequence == Indices var indices: Indices { get } // 有索引组成的序列 func prefix(upTo: Index) -> SubSequence // 指定位置之前的子列 func suffix(from: Index) -> SubSequence // 指定位置开始的子类 var isEmpty: Bool { get } // 是否为空 var count: IndexDistance { get } // 元素数量 var first: Element? { get } // 开头的元素 func index(_:Index, offsetBy: IndexDistance) -> Index // 获取新的索引 func distance(from: Index, to: Index) -> IndexDistance // 索引间的差 } ● associatedtype IndexDisrance = Int 指定默认型,没有声明且无法推导时候使用该型来作为IndexDisrance ● Index是索引的型,不一定是整数 ● 改变collection(改变内部元素)的方法(上面没写), 只有SubSequence和采用Collection的类型相同时才能用 ● 主要属性(数组, 哈希表, 字符串等) T: 元素的类型(Element), S: 采用Sequence的类型, Index: 索引的类型
● 主要方法(数组, 哈希表, 字符串等)
● 管理多个值的协议主要有(不是全部) Sequence <-- Collection <-- BidrectionalCollection <-- RangeReplaceableCollection <-- 其他... 实际开发时候基本上数组(Array)就够用了 # TODO: Supply [序列与闭包的组合, 使程序简洁 s13.4]
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
值型数据的共有 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Copy-On-Write |
● 写时复制, 平时数据交换是指针,只有在需要改写新处或者原处时复制一个新的来写 加快运行速度 ● 写时复制相对于直接写的好处是写失败不会对先前版本有影响 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
值型数据的共有 |
● Swift的值型数据使用Copy-On-Write ● 子列的也是Copy-On-Write Sequence, SubSequence SubSequence基本上和Sequence一样 String, SubString Array, ArraySlice 好处: ● 保留共有指针的高速 ● 防止了因为子列的参照而使得主列无法释放 因为不同型, 子列变到主列触发Copy机制, 子列与元主列不再被新变量参照 方针: 每次都变换拖慢运行速度,所以函数的参数, 返回值, 向属性代入等变换为主列 短期内部处理不用转换 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||