swift-泛型与集合
一、认识泛型
1.1 泛型的基本写法
首先我们需要指定一个占位符T,紧挨着写在函数名后面的一对尖括号表示T遵循的协议,其次我们用T来替换任意定义的函数形式参数。
func multiNumInt(x:Int,y:Int)->Int{ return x*y } func multiNumInt<T:FloatingPoint>(x:T,y:T)->T{ return x*y }
如果一个栈可以实现任意数据结构的存储,那么这个栈用泛型怎么定义呢?我们先看看正常的栈是怎么定义的
struct Stack{ var items = [Int]() mutating func push(_ item: Int){ items.append(item) } mutating func pop()->Int?{ if items.isEmpty{ return nil } return items.removeLast() } }
接下来我们定义一个协议栈:
struct Stack<T>{ var items = [T]() mutating func push(_ item: T){ items.append(item) } mutating func pop()->T?{ if items.isEmpty{ return nil } return items.removeLast() } }
1.2 协议的泛型
我们首先将上面的栈的相同行为定义为一个协议
protocol StackProtocol{ var itemCount: Int{get} mutating func push() -> Int mutating func pop() -> Int? func index(of index: Int) -> Int }
我们试试是不是可以将协议也定义为泛型?
我们通过验证发现,协议是不能定义为泛型的,要使用关联对象,才可以使用
那关联类型是否可以添加约束呢?答案是肯定的
protocol StackProtocol{ associatedtype T:FixedWidthInteger var itemCount: Int{get} mutating func push() -> Int mutating func pop() -> Int? func index(of index: Int) -> Int }
当然,我们也可以在约束中使用协议,我们可以看到Even是一个关联类型,但是他的约束中涉及到了两个,第一个是需要遵守EventProtocol协议,第二个是通过where分句来保证指定的类型形式参数和关联类型必须相同。
protocol EventProtocol:StackProtocol{ associatedtype Even:EventProtocol where Even.T == T func pushEven(item:Int)->Even }
1.3 类型擦除
首先,我们先来看下面一段代码(场景是需要请求用户数据)
我们可以看到程序中报错了,这意味着协议DataFetch只能作泛型约束,不能作为具体约束类型,因为编译器无法确定DataFetch的具体类型是什么,那么我们可以改成userdata
protocol DataFetch{ associatedtype dataType func fetch(completion:((Result<dataType,Error>)->Void)?) } struct User{ let userID:Int let name:String } struct userData:DataFetch{ typealias dataType = User func fetch(completion: ((Result<User, Error>) -> Void)?) { let user = User(userID: 11, name: "swift") completion?(.success(user)) } } //我们通过userdata来拿到user数据 class someViewController{ let userData:userData init(userData:userData){ self.userData = userData } }
这样来看确实是没有问题的,但是这样someViewController和UserData之间的耦合性就严重了,此时我们可以通过引入一个中间层的方式来解决
protocol DataFetch{ associatedtype dataType func fetch(completion:((Result<dataType,Error>)->Void)?) } struct User{ let userID:Int let name:String } //引入中间层来解决 struct AnyDataFetch<T>:DataFetch{ typealias dataType = T let _fetch:(((Result<dataType,Error>)->Void)?)->Void init <U:DataFetch>(_fetchable:U) where U.dataType == T{ //需要实现的类型在这里注入 _fetch = _fetchable.fetch } func fetch(completion: ((Result<T, Error>) -> Void)?) { //转发协议的抽象类型 _fetch(completion) } } struct userData:DataFetch{ typealias dataType = User func fetch(completion: ((Result<User, Error>) -> Void)?) { let user = User(userID: 11, name: "swift") completion?(.success(user)) } } struct VIPData:DataFetch{ typealias dataType = User func fetch(completion: ((Result<User, Error>) -> Void)?) { let user = User(userID: 11, name: "VIP") completion?(.success(user)) } } //我们通过userdata来拿到user数据 class someViewController{ let userData:AnyDataFetch<User> init(userData:AnyDataFetch<User>){ self.userData = userData } } let vipUser = VIPData() let anyUser = AnyDataFetch<User>(_fetchable: vipUser) //此时的VC就可以穿入不同的user类型,不需要关心具体类型 let vc = someViewController.init(userData: anyUser)
- 在这里,我们定义了一个中间层结构AnyDataFetch,他实现了DataFetch的所有方法
- 在AnyDataFetch的初始化过程中,实现协议的类型会被当作参数传入
- 在AnyDataFetch实现的具体协议方法Fetch中,再转发实现协议的抽象类型
- 这时候,AnyDataFetch就是一个具体类型,而非泛型,即完成了类型擦除
二、泛型原理
泛型相当于是用T提前占据了内存位置,那么泛型是通过什么来计算传入的类型以及大小的呢?
struct ValueWitnessTable { var initializeBufferWithCopyOfBuffer:UnsafeRawPointer var destroy:UnsafeRawPointer var initializeWithCopy:UnsafeRawPointer var assignWithCopy:UnsafeRawPointer var initializeWithTake:UnsafeRawPointer var assignWithTake:UnsafeRawPointer var getEnumTagSinglePayload:UnsafeRawPointer var storeEnumTagSinglePayload:UnsafeRawPointer var size:Int var stride:Int var flags:Int var targrtMetadata:TargetMetadata }
泛型通过 valueWItnessable来管理内存,我们可以还原出 valueWItnessable
三、Swift集合
3.1 Sequence协议
Sequence协议表达的可以是一个有限的集合,也可以是一个无限的集合,而且他只提供集合中的元素以及如访问元素的接口,关系如下所示:
在研究Sequence之前,我们先从一些简单的代码入手:
let numbers = [1,2,3,4,5] for number in numbers{ print(number) }
这是一个非常常见的for循环,但是我们需要思考一个问题,在for循环的底层是如何进行遍历的?我们通过sil源码来观察一下
我们不难看出,for循环主要调用的是.next()方法
for in本质上是个语法糖,通过Sequence创建一个迭代器,然后把当前数组传给迭代器,最后调用迭代器的next方法将数组的元素遍历出来
3.2 Sequence
接下来,我们自定义一个遵从Sequence的迭代器
- 有限的集合
struct LGSequence:Sequence{ typealias Element = Int var arrayCount:Int //对于sequence来说,需要创建一个迭代器,而对于迭代器来说遍历当前集合中的元素 所以我们需要提供一个遍历的迭代器 func makeIterator() -> LGIterator { return LGIterator(_sequence: self) } } //迭代器接受一个Sequence类型返回 通过next方法将元素返回 struct LGIterator:IteratorProtocol{ typealias Element = Int let sequence:LGSequence init(_sequence:LGSequence) { self.sequence = _sequence } var count = 0 mutating func next() -> Element? { guard count < sequence.arrayCount else{ return nil } count += 1 return count } } var s = LGIterator(_sequence: LGSequence(arrayCount: 10)) while let x = s.next(){ print(x) }
// 1 2 3 4 5 6 7 8 9 10
- 无限的集合
struct unlimitedIterator:IteratorProtocol{ typealias Element = Int let value:Int func next() -> Element? { return value } } var iterator = unlimitedIterator(value: 10) while let x = iterator.next(){ print(x) }
// 10 10 10 ....
3.3 Collection协议
Swift中的Collection协议是建立在Sequence协议之上的,为有限的序列提供下标访问的能力,同时增加了count属性,自定义索引的特性
- 以环形数组为例
extension FixedWidthInteger { /// Returns the next power of two. @inlinable func nextPowerOf2() -> Self { guard self != 0 else { return 1 } // return 1 << (Self.bitWidth - (self - 1).leadingZeroBitCount) } } struct RingBuffer<Element>{ //internal只能访问自己模块的任何internal实体,不能访问其他模块中的internal实体 //ContiguousArray相当于swift自己的array,比oc的效率要高 //headIndex相当于头指针 //tailindex相当于尾指针 internal var _buffer: ContiguousArray<Element?> internal var headIndex: Int = 0 internal var tailIndex: Int = 0 internal var mask: Int{ return self._buffer.count - 1 } init(initalCapacity: Int) { //nextPowerOf2 == 2的n次 let capcatiy = initalCapacity.nextPowerOf2() self._buffer = ContiguousArray<Element?>.init(repeating: nil, count:capcatiy) } //移动尾指针 mutating func advancedTailIndex(by: Int){ self.tailIndex = self.indexAdvanced(index: self.tailIndex, by: by) } //移动头指针 mutating func advancedHeadIndex(by: Int){ self.headIndex = self.indexAdvanced(index: self.headIndex, by: by) } func indexAdvanced(index: Int, by: Int) -> Int{ return (index + by) & self.mask } mutating func append(_ value: Element){ _buffer[self.tailIndex] = value self.advancedTailIndex(by: 1) if self.tailIndex == self.headIndex { fatalError("out of bounds") } } mutating func read() -> Element?{ let element = _buffer[self.headIndex] self.advancedHeadIndex(by: 1) return element } }
- MutableCollection 允许集合通过下标修改自身元素
extension RingBuffer: Collection, MutableCollection{ var startIndex: Int{ return self.headIndex } var endIndex: Int{ return self.tailIndex } subscript(position: Int) -> Element? { get{ //获取值 return self._buffer[position] } set{ //通过下标修改值 self._buffer[position] = newValue } } //移动当前索引的位置 func index(after i: Int) -> Int { return (i + 1) & self.mask } }
- RangeReplaceCollection 允许集合修改任意区间元素
extension RingBuffer:RangeReplaceableCollection{ init() { self.init(initalCapacity: 10) } //移除元素 mutating func remove(at position: Int) -> Element? { var currentIndex = position let element = self._buffer[position] switch position{ //当要删除的结点和头结点重合时,直接头结点右移动一位 case self.headIndex: self.advancedHeadIndex(by: 1) self._buffer = nil default: self._buffer[position] = nil var nextIndex = self.indexAdvanced(index: position, by: 1) while nextIndex != self.tailIndex{ self._buffer.swapAt(currentIndex, nextIndex) currentIndex - nextIndex nextIndex = self.indexAdvanced(index: currentIndex, by: 1) } self.advancedHeadIndex(by: -1) } return element } }
-
BidirectionalCollection 可以向前或者向后遍历元素
extension RingBuffer: BidirectionalCollection{ func index(before i: Int) -> Int { return (i - 1) & self.mask } }
-
RandomAccessCollection 可以任意访问集合元素
extension RingBuffer: RandomAccessCollection{ func index(_ i: Int, offsetBy distance: Int) -> Int { return (i + distance) & self.mask } func distance(from start: Int, to end: Int) -> Int { return end - start } }