swift标准库中常见的55个协议。
2021-02-20 21:19
swift标准库中常见的55个协议。
- 从协议名结尾字面上我们可以将Protocol分为able、Type、Convertible 三类
从功能角度上来讲的话可以总结为:
- 功能添加型(able结尾) "可以做什么?"
举例:
- Hashable: 给你的实例添加一个生成哈希值的功能。
- Equatable: 给你的实例添加一个判断相等的功能。
2.类型对比型(Type结尾) "这个实例是什么?"
举例:
- CollectionType: swift中所有的集合类型都要遵守的一个协议,告诉大家你的实例是一个集合类型的。
- 类型转换型(Convertable结尾) "这个实例可以转化成什么?"
举例:
- CustomStringConvertible: Print打印输出时比较常用的一个协议,这个协议可以将你的实例需要打印的信息转化成字符串。
我们在以后的日常中可以通过一个协议的命名方式大概推算出这个协议的作用。
Tips: 在项目开发中自定义协议的时候,命名规范就可以依据这三个方面来做协议的命名
内建集合类型
- Set
基本定义:一组无序元素,且元素不会重复。可以看成是只有key没有value的字典。
项目使用场景:前几天才刚刚用字典的形式保存了用户学习课程的天数信息。现在看来,用set的形式去存储这些信息最为方便。。
集合代数:
和数学上的集合概念类似,set也有交并补的方法
并集 union() 交集 intersection() 补集 subtracting()
使用方法比较简单,这里直接把三个方法写到一起了
let numSet:Set = [1,2,3,4,5]
let otherSet:Set = [2,6]
///并集
let unionSet = numSet.union(otherSet)
print(unionSet) //[5, 6, 2, 3, 1, 4]
///交集
let intersectionSet = numSet.intersection(otherSet)
print(intersectionSet) //[2]
///补集
let subtractingSet = numSet.subtracting(otherSet)
print(subtractingSet) //[5, 3, 1, 4]
索引集合和字符集合
Foundation框架中实现了SetAlgebra协议的一共有三类:Set IndexSet CharacterSet
SetAlgebra 可以做什么? 提供了一些基础的数学运算:如 == 、contains(是否包含),交并补、subtract(剔除)等。
IndexSet
表示一个由正整数组成的集合,我们其实可有用Set 达到同等效果。 但IndexSet更加 高效 ,内部使用了 一组范围列表 进行实现。 eg.
存储1到1000
set可能是[1,2,3,4,......,1000]
IndexSet的内部只是真正存储了1,1000 首位和末位两个数字。 它会存储```连续的范围```。So 会更加高效
CharacterSet
是一个高效存储 Unicode 的字符集合。后面讲String的时候会具体讲的~
在闭包中使用集合
我们可以利用Set去给Sequence写一个扩展,来获取序列中所有唯一元素
Range
定义:两个值的区间 ..
let singleNum = 0..
let lowerLetters = Character("a")...Character("z")//包括z
注: 上面创建的两个都是可数范围CountableRange。这种类型类型是可以被迭代的。
- 数组
数组的可变性:
数组和其他的集合一样,具有值语义,数组赋值时,这个数组的内容会被复制,如下:
swift:
var x = [6,6,6]
var y = x
y.append(6)
y // [6,6,6,6]
x // [6,6,6]
OC:
// NSMutableArray *x = [NSMutableArray arrayWithArray:@[@"1",@"2",@"3"]];
// NSMutableArray *y = x;
// [y addObject:@"4"];
// NSLog(@"x=%@",x); //1,2,3,4
// NSLog(@"y=%@",y); //1,2,3,4
swift中 Array是以struct的形式存在的。并非OC里面的class
实际上swift集合类型都使用的“写时复制”技术。
只有在复制的时候复制出来,其他时候都共享一个内部存储。
- 数组和可选值
swift中不建议直接使用下标去访问一个数组
通过swift中一些操作去操作一个数组
1.迭代除第1个元素外的数组元素:for x in array.dropFirst()
2.迭代除最后5个元素外的数组元素:for x in array.dropLast(5)
3.(项目中最常用到的一个方法)所有元素和其下标: for (idx, obj) in array.enumerated()
4.寻找指定元素的位置 if let idx = array.index {someMatchingLogic($0) }
5.所有元素进行变形 array.map {someTransformation($0)}
6.筛选符合某个标准的元素 array.filter {someCriteria($0)}
7.两个数组变形合并 flatMap
swift不鼓励你去做索引计算,当在项目中写道array[idx]时,可以思考一下有没有更好的方法去解决。
切片 slice:
获取某个范围中的元素,我们可以使用切片 例如:获取除了第一个元素以外的元素集合
let fruit = ["??","??","??"]
let slice = fruit[1.. print(slice)//["??", "??"]
print("\(type(of: slice))")//ArraySlice
得到的类型是ArraySlice 而不是Array,其实切片只是Array的一种表现形式。我们在开发过程中可以把它当做数组来看。 类型转换直接Array(ArraySlice)
集合类型协议 序列
序列: sequence
满足sequence协议只需提供一个返回迭代器(iterator):的makeIterator方法。 集合
迭代器:Iterator
里面只有一个方法next( )
public protocol IteratorProtocol {
///关联类型 Element 指定了迭代器产生的值的类型
///如 subViews的迭代原生类型就是View 可以不用写,编译器根据next()的返回值自动判断。
associatedtype Element
///你只需要在每次调用的时候返回下一个值,结束时返回nil
public mutating func next() -> Self.Element?
}
next( )方法是用 mutating 关键字修饰。 几乎所有的迭代器都要求是可变状态,这样才可以管理在序列中的当前位置
注:迭代器是单向结构,只能按照增加的方向前进,不能倒退或者重置。
准守序列(sequence)协议自定义迭代器和自定义集合
第一步: 创建一个迭代器(Iterator)
struct PrefixStrIterator:IteratorProtocol {
var string: String
var offset: String.Index
init(string:String) {
self.string = string
offset = string.startIndex
}
///写协议方法
mutating func next() -> String? {
guard offset offset = string.index(after: offset)
return String(string[string.startIndex.. }
}
第二步: 创建一个使用这个迭代器的序列(sequence)
///step2.创建一个属于你的集合
struct PrefixSequence: Sequence {
var string: String
///协议方法:返回一个迭代器
func makeIterator() -> PrefixStrIterator {
return PrefixStrIterator(string: string)
}
}
第三步: run
///myfirstSquence 我的第一个集合
for prefixStr in PrefixSequence(string: "Hi~LiaoWorking!") {
print(prefixStr)
}
- 迭代器和值语义
值语义 struct,应该还有其他类型。待补充。 赋值时不存在引用,复制了一份过去了 引用语义 class 赋值时存在引用 注:但AnyIterator这个是引用对象。
- 基于函数的迭代器和序列
AnyIterator还有一个初始化方法就是直接接受next()函数来当做参数。然后通过引用语义的特性,可以不创建新的类型就写一个斐波那契迭代器
/// 通过引用语义的特性写斐波那契
func fibsIterator() -> AnyIterator {
var startNum = (0, 1)
return AnyIterator{
let nextNum = startNum.0
startNum = (startNum.1 , startNum.0 + startNum.1)
return nextNum
}
}
无限序列
序列可以是无限的,而集合是有限的。
不稳定序列
Sequence文档明确指出序列并不保证能被多次遍历。 原因是Sequence协议并不关心里面的序列元素会不会销毁。 这就是为什么在集合类型中.first 在序列中并不存在。
序列sequence和迭代器Iterator的关系
迭代器Iterator可以看成即将返回的元素组成的不稳定序列sequence
子序列
Sequence还有一个关联类型 SubSequence
在返回原序列Sequence的切片slice操作中,SubSequence会被当做返回值的子类。
SubSequence 有一些常用方法
prefix
suffix
dropFirst
dropLast
split
-
for 循环 Swift实现了两个范围操作符,闭合操作符和半开操作符。 第一个包括范围中的所有值。 例如,以下包括从0到4的所有整数: 0 ... 4 半开操作符不包括最后一个元素。 以下产生相同的0到4结果: 0 ..
-
值类型、引用类型
struct Tutorial {
var difficulty: Int = 1
}
var tutorial1 = Tutorial()
var tutorial2 = tutorial1
tutorial2.difficulty = 2
for _ in 0...4 {
print("Hello!")
}
tutorial1.difficulty是1,而tutorial2.difficulty是2。 Swift中的结构是值类型,它们通过值而不是引用来复制
如果Tutorial是一个类,tutorial1.difficulty和tutorial2.difficulty将是2.在Swift中的类是引用类型。 对tutorial1的属性的任何更改都将反映到tutorial2中,反之亦然。
- var与let
var view1 = UIView()
view1.alpha = 0.5
let view2 = UIView()
view2.alpha = 0.5
view1是一个变量,可以重新分配给一个新的UIView实例。 通过let声明只能赋值一次,所以下面的代码不编译:
view2 = view1 // Error: view2 is immutable
但是,UIView是一个具有引用语义的类,所以你可以改变view2的属性(这意味着最后一行将编译):
let view2 = UIView() view2.alpha = 0.5 // Yes!
- 闭包简化步骤
let animals = ["fish", "cat", "chicken", "dog"]
let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in
return one }
第一个简化与参数有关。类型推理系统可以计算闭包中的参数的类型,所以你可以摆脱它们:
let sortedAnimals = animals.sort { (one, two) -> Bool in return one
返回类型也可以推断,所以放弃它:
let sortedAnimals = animals.sort { (one, two) in return one
$ i表示法可以替换参数名称:
let sortedAnimals = animals.sort { return $0
在单语句闭包中,可以省略return关键字。最后一条语句的返回值成为闭包的返回值:
let sortedAnimals = animals.sort { $0
对于字符串,有一个比较函数定义如下:
func Bool
这个整洁的小函数使你的代码像下面这样容易:
let sortedAnimals = animals.sort(
请注意,此渐进的每个步骤都会编译并输出相同的结果,并且创建了一个字符闭包!
上一篇:排序算法之快速排序
下一篇:线程、进程间通信(2)