Swift底层原理-Sequence与Collection

Swift底层原理-Sequence与Collection

  • Sequence协议来说,表达的是既可以是一个有限的集合,也可以是一个无限的集合,而它只需要提供集合中的元素和如何访问这些元素的接口即可。
  • Collection协议是建立在Sequence协议之上的,为有限的序列提供下标访问的能力,同时增加了count属性,自定义索引等特性

[图片上传失败...(image-e4a786-1684654022077)]

Sequence

  • Sequence作为swift集合类协议扩展方法,为集合提供了一系列的序列迭代能力。

for in本质

  • Sequence是通过Iterator来访问元素的。Iterator是一个迭代器,我们来看一段代码,如下:
let nums = [1, 2, 3, 4, 5];
for element in nums {
    print(element)
}
  • Swift中的 for in 其实是一个语法糖,那么它的本质是什么呢,我们把它编译成sil的代码来看一下
// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @$s4main4numsSaySiGvp              // id: %2
  // 省略部分代码
  %49 = alloc_stack $Array<Int>                   // users: %53, %52, %50
  store %48 to [init] %49 : $*Array<Int>          // id: %50
  %51 = witness_method $Array<Int>, #Sequence.makeIterator : <Self where Self : Sequence> (__owned Self) -> () -> Self.Iterator : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator // user: %52
  %52 = apply %51<[Int]>(%47, %49) : $@convention(witness_method: Sequence) <τ_0_0 where τ_0_0 : Sequence> (@in τ_0_0) -> @out τ_0_0.Iterator
  dealloc_stack %49 : $*Array<Int>                // id: %53
  br bb1                                          // id: %54

bb1:                                              // Preds: bb3 bb0
  %55 = alloc_stack $Optional<Int>                // users: %61, %60, %58
  %56 = begin_access [modify] [unknown] %47 : $*IndexingIterator<Array<Int>> // users: %59, %58
  %57 = witness_method $IndexingIterator<Array<Int>>, #IteratorProtocol.next : <Self where Self : IteratorProtocol> (inout Self) -> () -> Self.Element? : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element> // user: %58
  %58 = apply %57<IndexingIterator<Array<Int>>>(%55, %56) : $@convention(witness_method: IteratorProtocol) <τ_0_0 where τ_0_0 : IteratorProtocol> (@inout τ_0_0) -> @out Optional<τ_0_0.Element>
  end_access %56 : $*IndexingIterator<Array<Int>> // id: %59
  %60 = load [trivial] %55 : $*Optional<Int>      // user: %62
  dealloc_stack %55 : $*Optional<Int>             // id: %61
  switch_enum %60 : $Optional<Int>, case #Optional.some!enumelt: bb3, case #Optional.none!enumelt: bb2 // id: %62
  • 我们可以看到在%51行,调用了Sequence.makeIterator方法,创建一个Iterator,把数组传给迭代器。
  • %57行,调用IteratorProtocol.next方法,将数组元素遍历出来。

Sequence与IteratorProtocol

  • 我们来Sequence.swift这个文件,查看Sequence定义
public protocol Sequence {
  // 可在协议实现后确定协议类型
  associatedtype Element

  associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
  
  // 获取一个迭代器
  /// Returns an iterator over the elements of this sequence.
  __consuming func makeIterator() -> Iterator

  // 省略其他方法
}
  • 在该协议中,最重要的就是创建了一个迭代器

  • 查看一下IteratorProtocol定义

public protocol IteratorProtocol {
    /// The type of element traversed by the iterator.
    associatedtype Element

    mutating func next() -> Self.Element?
}
  • 它有一个next方法,可以通过调用next方法来返回元素。
  • 所以我们每次在使用 for..in 的时候,其实都是 通过sequence创建一个迭代器,用这个集合的迭代器来遍历当前集合或者序列当中的元素

自己定义一个遵循Sequence的结构体

  • 自定义可迭代结构体
struct TestSequence: Sequence {
    typealias Element = Int
    typealias Iterator = TestIterator
    let count: Int
    
    // MARK: - initialization
    init(count: Int) {
        self.count = count
    }
    
    func makeIterator() -> TestIterator {
        return TestIterator(sequece: self)
    }
}

struct TestIterator: IteratorProtocol {
    typealias Element = Int
    let sequece: TestSequence
    var count = 0
    
    // MARK: - initialization
    init(sequece: TestSequence) {
        self.sequece = sequece
    }
    
    mutating func next() -> Int? {
        guard count < sequece.count else {
            return nil
        }
        count += 1
        return count
    }
}

let seq = TestSequence(count: 5)
for element in seq {
    print(element)
}

打印结果:

1

2

3

4

5

Collection

  • Collection协议实现了Sequence协议,为有限的序列提供下标访问的能力,同时增加了count属性,自定义索引等特性。

  • Collection是一个序列,其元素可以被多次遍历。通过定义startIndexendIndex属性,表示集合起始和结束位置。

  • 我们看一下colletcion定义

public protocol Collection: Sequence {
  // FIXME: ideally this would be in MigrationSupport.swift, but it needs
  // to be on the protocol instead of as an extension
  @available(*, deprecated/*, obsoleted: 5.0*/, message: "all index distances are now of type Int")
  typealias IndexDistance = Int  

  // FIXME: Associated type inference requires this.
  override associatedtype Element

  associatedtype Index: Comparable

  var startIndex: Index { get } 

  var endIndex: Index { get }

  // sequence协议的实现
  associatedtype Iterator = IndexingIterator<Self>

  override __consuming func makeIterator() -> Iterator

  associatedtype SubSequence: Collection = Slice<Self>
  where SubSequence.Index == Index,
        Element == SubSequence.Element,
        SubSequence.SubSequence == SubSequence

  func index(after i: Index) -> Index
   
  // 省略部分方法
}
  • 遵循Collection协议,此时我们就需要实现 startIndexendIndexindex(after:) 方法,index(after:) 是为了便于移动当前索引的位置。

mutableCollection

  • mutableCollection定义
public protocol MutableCollection: Collection
where SubSequence: MutableCollection
{
  // FIXME: Associated type inference requires these.
  override associatedtype Element
  override associatedtype Index
  override associatedtype SubSequence

  @_borrowed
  override subscript(position: Index) -> Element { get set }

  override subscript(bounds: Range<Index>) -> SubSequence { get set }

  mutating func partition(
    by belongsInSecondPartition: (Element) throws -> Bool
  ) rethrows -> Index

  mutating func swapAt(_ i: Index, _ j: Index)
  
  mutating func _withUnsafeMutableBufferPointerIfSupported<R>(
    _ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
  ) rethrows -> R?

  mutating func withContiguousMutableStorageIfAvailable<R>(
    _ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
  ) rethrows -> R?
}
  • 遵循该协议,实现了下标的setter方法,便于在语法上直接通过下标来访问并修改这个元素的值。

RangeReplaceableCollection

  • RangeReplaceableCollection允许集合修改任意区间的元素
public protocol RangeReplaceableCollection: Collection
  where SubSequence: RangeReplaceableCollection {
  // FIXME: Associated type inference requires this.
  override associatedtype SubSequence

  /// Creates a new, empty collection.
  init()

  mutating func replaceSubrange<C>(
    _ subrange: Range<Index>,
    with newElements: __owned C
  ) where C: Collection, C.Element == Element

  mutating func reserveCapacity(_ n: Int)

  init(repeating repeatedValue: Element, count: Int)

  init<S: Sequence>(_ elements: S)
    where S.Element == Element

  mutating func append(_ newElement: __owned Element)

  mutating func append<S: Sequence>(contentsOf newElements: __owned S)
    where S.Element == Element

  mutating func insert(_ newElement: __owned Element, at i: Index)
 
  @discardableResult
  mutating func remove(at i: Index) -> Element

  mutating func removeSubrange(_ bounds: Range<Index>)

  mutating func _customRemoveLast() -> Element?

  mutating func _customRemoveLast(_ n: Int) -> Bool

  @discardableResult
  mutating func removeFirst() -> Element

  mutating func removeFirst(_ k: Int)

  mutating func removeAll(keepingCapacity keepCapacity: Bool /*= false*/)

  // 省略部分方法
}
  • 除此之外还有很多针对集合的协议,比如说BidirectionalCollection可以向前或向后遍历集合;RandomAccessCollection可以任意访问集合元素等。
  • 根据功能的不同划分,定义在不同的协议里面,符合借口单一原则,通过协议的组合,可以达到不同复杂度的集合。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容