String 里,用来索引 Character 的,不是整数,而是StringIndex

内部结构

  1. extension String {
  2. /// A position of a character or code unit in a string.
  3. @_fixed_layout
  4. public struct Index {
  5. @usableFromInline
  6. internal var _rawBits: UInt64
  7. @inlinable @inline(__always)
  8. init(_ raw: UInt64) {
  9. self._rawBits = raw
  10. self._invariantCheck()
  11. }
  12. }
  13. }

其实就是一个UInt64的值,不过不同的bit有不同的含义。

  1. String's Index has the following layout:
  2. ┌──────────┬───────────────────┬────────────────┬──────────┐
  3. │ b63:b16 │ b15:b14 │ b13:b8 │ b7:b0 │
  4. ├──────────┼───────────────────┼────────────────┼──────────┤
  5. │ position │ transcoded offset │ grapheme cache │ reserved │
  6. └──────────┴───────────────────┴────────────────┴──────────┘
  7. - grapheme cache: A 6-bit value remembering the distance to the next grapheme
  8. boundary
  9. - position aka `encodedOffset`: An offset into the string's code units
  10. - transcoded offset: a sub-scalar offset, derived from transcoding
  • position,即当前字符的相对于起始位置的偏移,以 code unit 计算。在String里,默认是utf8编码,所以指代距离起始位置的字节数。
  • transcoded offset 和编码相关
  • grapheme cache 标记当前字符距离下一个字符的距离。

起始和结束位置的定义

  1. // Index
  2. extension _StringGuts {
  3. @usableFromInline
  4. internal typealias Index = String.Index
  5. @inlinable
  6. internal var startIndex: String.Index {
  7. @inline(__always) get { return Index(encodedOffset: 0) }
  8. }
  9. @inlinable
  10. internal var endIndex: String.Index {
  11. @inline(__always) get { return Index(encodedOffset: self.count) } //count 不是String 的count
  12. }
  13. }

即指向了内存地址的起始和结束。

计算字符串的长度

String 的长度,根据文档说,不可以在O(1)时间内获得,因为要遍历整个字符串。string遵守BidirectionalCollection,而不是RandomAccessCollection

  1. /// The number of characters in a string.
  2. public var count: Int {
  3. @inline(__always) get {
  4. return distance(from: startIndex, to: endIndex)
  5. }
  6. }

从定义中可以看到,为了计算长度,需要计算两个Index 之间的距离。
最终是需要从startIndex 遍历到 endIndex的。

  1. @inlinable // protocol-only
  2. internal func _distance(from start: Index, to end: Index) -> Int {
  3. var start = start
  4. var count = 0
  5. if start < end {
  6. while start != end {
  7. count += 1
  8. formIndex(after: &start)
  9. }
  10. }
  11. else if start > end {
  12. while start != end {
  13. count -= 1
  14. formIndex(before: &start)
  15. }
  16. }
  17. return count
  18. }

每一次formIndex都会调用到下面的代码:

  1. public func index(after i: Index) -> Index {
  2. _precondition(i < endIndex, "String index is out of bounds")
  3. // TODO: known-ASCII fast path, single-scalar-grapheme fast path, etc.
  4. let stride = _characterStride(startingAt: i)
  5. let nextOffset = i.encodedOffset &+ stride
  6. let nextStride = _characterStride(
  7. startingAt: Index(encodedOffset: nextOffset))
  8. return Index(
  9. encodedOffset: nextOffset, characterStride: nextStride)
  10. }

可以看到,需要确定当前字符占用code unit 的个数,以及下一个字符占用code unit的个数。
这样子逐一遍历下去,不能在常数时间内完成也就可想而知了。

Swift 里字符串(七)stringIndex的更多相关文章

  1. Swift 里字符串(十)修改字符串

    以append操作为例 public mutating func append(_ other: String) { if self.isEmpty && !_guts.hasNati ...

  2. Swift里字符串(五)Native strings

    Native strings have tail-allocated storage, which begins at an offset of nativeBias from the storage ...

  3. Swift 里字符串(三)small String

     small string, 只有两个 UInt64 的字,这里面存储了所有的信息. 内存布局如下:  第二个 UInt64 存储了标记位和长度信息,以及部分字符串的值 // Get an int ...

  4. Swift 里字符串(四)large sting

    对于普通的字符串,对应的_StringObject 有两个存储属性: _countAndFlagsBits: UInt64 _object: Builtin.BridgeObject _countAn ...

  5. Swift 里字符串(一)概览

    感受一下字符串相关的源文件个数  String 概览 是一个结构体 只有一个变量,类型是 _StringGuts  如上所示,String 真正的内容在__StringStorage或者__Sha ...

  6. Swift 里字符串(九)UTF16View

    即以 UTF16 编码的格式来查看字符串. UTF16View 是一个结构体 @_fixed_layout public struct UTF16View { @usableFromInline in ...

  7. Swift 里字符串(八)UnicodeScalarView

    即以 Unicode Scarlar 的方式来查看字符串. /// let flag = "

  8. Swift里字符串(六)Shared strings

    Shared strings do not have tail-allocated storage, but can provide access upon query to contiguous U ...

  9. Swift 里字符串(十一)OC 字符串和 Swift 字符串的转换

     to OC func _bridgeToObjectiveCImpl() -> AnyObject { if _guts.isSmall { return _guts.asSmall.wit ...

随机推荐

  1. 10 Maven 版本管理

    Maven 版本管理 一个健康的项目通常有一个长期.合理的版本演变过程.例如 Maven 本身的版本也比较多,如最早的 Maven1:Maven2 有 2.0.9.2.0.10.2.1.0.2.2.0 ...

  2. OSGi karaf scheduler

    OSGi karaf scheduler karaf 中提供了定时任务管理,只需安装 feature:install scheduler 即可,然后在 karaf 容器中发布 org.apache.k ...

  3. swift 添加webview

    swift显示HTML代码 在布局中的方法 1.根据URL  直接显示内容, var urls : NSURL = NSURL(string: "http://www.baidu.com&q ...

  4. 2、HttpClient修改处理策略Strategy

    HttpClient提供了很多接口,让我们能自定义处理逻辑,这些接口可以在AbstractHttpClient中找到: setAuthSchemes(AuthSchemeRegistry); setC ...

  5. how to enable the Accessibility in the app

    第一部分 先要装一个accchecker,全称是 UI Accessibility Checker .下载地址: http://acccheck.codeplex.com/ 装了之后 用这个工具可以 ...

  6. 2018.09.26洛谷P3957 跳房子(二分+单调队列优化dp)

    传送门 表示去年考普及组的时候失了智,现在看来并不是很难啊. 直接二分答案然后单调队列优化dp检验就行了. 注意入队和出队的条件. 代码: #include<bits/stdc++.h> ...

  7. 手动安装jar到maven

    mvn install:install-file -DgroupId=com.chinacloud.mir.common -DartifactId=one-aa-sdk -Dversion=1.0-S ...

  8. 14)settings.xml

    1. User Level. ${user.home}/.m2/settings.xml 2. Global Level. ${maven.home}/conf/settings.xml <se ...

  9. Android属性动画之ValueAnimator的介绍

    之前两篇博客,介绍的是ObjectAnimator作用与某一个控件的某一个属性.但我们的ValueAnimator它本身并不会作用与任何一个属性,它本身也不会提供任何一种动画.它简单的来说,就是一个数 ...

  10. jupyterlab notebook区别

    https://juejin.im/entry/5b350e52f265da59bd5ed31e Windows 7(Windows 10)安装后anaconda 命令行jupyter lab 出现4 ...