前言
为文本字符串添加特性或者语法糖在各种编程语言中都很普遍。就拿大家都很熟悉的 C 语言举例,C 字符串本质是一个字符数组(characters array),但是每次输入字符串的时候不用输入 ['h','e','l','l','o'] ,直接打 hello 就可以了,因为这个操作编译器帮你做了。
更高级的语言比如 Swift 处理字符串就不仅仅是当做字符数组了,String 是一个完整的类型,并且有各种特性。我们先来看一下 String 的一个特性:substring。
简单的看一下 String
首先粗略的了解一下字符串的实现。下面的代码来自标准库中 String.swift :
public struct String { public var _core: _StringCore }
当然也有一些其他初始化设置,不过在声明里只有这一个存储属性!秘密一定都在 StringCore.swift 里:
public struct _StringCore { public var _baseAddress: UnsafeMutableRawPointer? var _countAndFlags: UInt public var _owner: AnyObject? }
在这个类型里还有很多其他东西,不过我们还是只关注存储属性:
_StringCore 的真实情况比这里提到的要复杂的多,但是通过上面的内容可以让我们更容易理解字符串的一些信息:字符串的内部存储和存储的大小(underlying storage and size)。
Substring
Swift 中要怎么创建一个 substring?最简单的方式就是通过下标从 string 取一段:
let str = "Hello Swift!" let slice = str[str.startIndex.. <str index="" str="" startindex="" nbsp="" offsetby:="" 5="" hello=""></str>
虽然很简单,但是代码看起来不太优雅。
String 的索引不是直观的整型,所以截取时的位置索引需要利用 startIndex 和 index(_:offsetBy:)获取。如果是从字符串开始位置截取,可以省略掉 startIndex :
let withPartialRange = str[.. <str index="" str="" startindex="" nbsp="" offsetby:="" 5="" still="" hello=""></str>
或者用 collection 中的这个方法:
let slice = str.prefix(5) // still "Hello"
要记住字符串也是 collection ,所以你可以用集合下的方法,比如 prefix(),suffix(), dropFirst() 等。
Substring 的内部原理
substring 一个神奇的地方是他们重用了父 string 的内存。你可以把 substring 理解为父 string 的其中一段。
举个例子,如果从一个 8000 个字符的字符串中截取 100 个字符,并不需要重新初始化 100 个字符的内存空间。
这也意味着你可能不小心就把父 string 的生命周期延长了。如果有一大段字符串,然后你只是截取了一小段,只要截取的小段字符串没有释放,大段的字符串也不会被释放。
Substring 内部到底是怎么做到的呢?
public struct Substring { internal var _slice: RangeReplaceableBidirectionalSlice <string></string>
内部的 _slice 属性保存着所有关于父字符串的信息:
// Still inside Substring internal var _wholeString: String { return _slice._base } public var startIndex: Index { return _slice.startIndex } public var endIndex: Index { return _slice.endIndex }
计算属性 _wholeString(返回整个父字符串),startIndex 和 endIndex 都是通过内部的 _slice 返回。
也可以看出 slice 是如何引用父字符串的。
Substring 转换为 String
最后代码里可能有很多 substring,但是函数的参数类型需要的是 string。Substring 转换到 string 的过程也很简单:
let string = String(substring)
因为 substrings 和它的父字符串共享同一个内存空间,猜测创建一个新字符串应该会初始化一片新的存储空间。那么 string 的初始化到底过程是怎样的呢。
extension String { public init(_ substring: Substring) { // 1 let x = substring._wholeString // 2 let start = substring.startIndex let end = substring.endIndex // 3 let u16 = x._core[start.encodedOffset.. <end encodedoffset="" nbsp="" 4a="" if="" start="" sameposition="" in:="" x="" unicodescalars="" end="" self="" 4b="" else=""></end>
把 substring 转换成 string 的步骤非常简单,但是你可能要考虑是不是一需要这样做。是不是进行 substring 操作的时候都要求类型是 string?如果对 substring 的操作都需要转成 string,那么轻量级的 substring 也就失去了意义。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。
本文向大家介绍深入理解swift变量和函数,包括了深入理解swift变量和函数的使用技巧和注意事项,需要的朋友参考一下 Swift 函数用来完成特定任务的独立的代码块。 Swift使用一个统一的语法来表示简单的C语言风格的函数到复杂的Objective-C语言风格的方法。 函数声明: 告诉编译器函数的名字,返回类型及参数。 函数定义: 提供了函数的实体。 swift 申明变量 var name =
本文向大家介绍深入理解Swift语言中的闭包机制,包括了深入理解Swift语言中的闭包机制的使用技巧和注意事项,需要的朋友参考一下 在 Swift 中的闭包类似于结构块,并可以在任何地方调用,它就像 C 和 Objective C 语言内置的函数。 函数内部定义的常数和变量引用可被捕获并存储在闭包。函数被视为封闭的特殊情况,它有 3 种形式。 在 Swift 语言闭合表达式,如下优化,重量轻语法风
本文向大家介绍深入理解Mybatis中的resultType和resultMap,包括了深入理解Mybatis中的resultType和resultMap的使用技巧和注意事项,需要的朋友参考一下 一、概述 MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是对外部Res
本文向大家介绍深入理解Tensorflow中的masking和padding,包括了深入理解Tensorflow中的masking和padding的使用技巧和注意事项,需要的朋友参考一下 TensorFlow是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(te
本文向大家介绍深入理解python中的浅拷贝和深拷贝,包括了深入理解python中的浅拷贝和深拷贝的使用技巧和注意事项,需要的朋友参考一下 在讲什么是深浅拷贝之前,我们先来看这样一个现象: 为什么我只对b进行修改,却影响到了a呢?看过我在之前的文章中就说过:序列中保存的都是内存的引用。 所以,当我们通过b去修改里面的空列表的时候,其实就是修改内存中的同一个对象,所以会影响到a。 代码验证无误,所以
tensorflow中的基本数学运算用法 import tensorflow as tf sess = tf.Session() a = tf.placeholder("float") b = tf.placeholder("float") c = tf.constant(6.0) d = tf.mul(a, b) y = tf.mul(d, c) print sess.run(y, feed_