你真的懂Swift中的String吗?

概述

String是Swift中的一个基本数据类型,String提供了多种方便操作比如字符串拼接 + , 字符串比较的 <= , == 等运算符。同时它桥接了NSString,也就是说String与NSString可以很好的进行转换使用as即可。但是二者还是有区别的,最大区别就是:

  • String 值类型

  • NSString 引用类型

    Strings in Swift 2

swift提供characters属性暴露字符集合视图,但是string与arr、set、dictonary是不同的

Different Than the Sum of Its Parts

添加combining mark character这种字符的时候,改变的是内容

var letters: [Character] = ["c", "a", "f", "e"]
var string: String = String(letters)

print(letters.count) // 4
print(string) // cafe
print(string.characters.count) // 4

此时添加上升字符U+0301 ´,字符串仍是4个字符,但是最后的字符是é

let acuteAccent: Character = "\u{0301}" // ´ COMBINING ACUTE ACCENT' (U+0301)

string.append(acuteAccent)
print(string.characters.count) // 4
print(string.characters.last!) // é

此时字符串 characters 里面不在含有e不再包含´,而是包含é

string.characters.contains("e") // false
string.characters.contains("´") // false
string.characters.contains("é") // true

Judged by the Contents of Its Characters

另一个不同是等式比较

  • 数组相等要求有相同的count、对应索引元素相等
  • 结合相等要求有相同的count、第一个集合包含在第二个里面
  • 字典相等要求二者有相同的key set,value pair

string相等是语义相等,呈现相等,而不管string是如何构造的,韩语下面是左+右=在下面的那个字

let decomposed = "\u{1100}\u{1161}" // ᄀ + ᅡ
let precomposed = "\u{AC00}" // 가

decomposed == precomposed // true

Depends on Your Point of View

string不是集合,但是他们提供了遵守集合类型的views

  • characters,字符集合
  • unicodeScalars,unicode字符集
  • utf8,utf8字符集
  • utf16,utf16字符集

如果以前面的cafe为例,那么里面的内容是这样的
[图片上传失败...(image-83f297-1513308927776)]

接下来细数用Swift重写OC版的NSString+Extention遇到的坑

Swift的数据类型可以说都是对象类型,对应最简单的Int类型如果你查看它的源码的话你会发现它是struct类型,当然String也不例外

首先我们来谈一下Swift中的方法

Swift的String有compare方法,但是你却不能再doc文档中找到

用过OC的同学都知道OC里面的NSString是有compare方法的,方法的原名是- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)compareRange locale:(nullable id)locale,你可以和我说String有运算符来表示啊,但是有时候我们在写方法封装的时候不想通过判断去辨别用哪个运算符,我们也想用option啊。。。

回想一下,Swift中的string类是与OC类桥接的,那么Swift本身又对Fundation进行了重塑,所以可以猜测String可以调用NSString的方法并且是以Swift的语法格式(.语法格式),所以在String 的 Doc 中看不到compare的语法声明当时却可以调用!!!

同样的,你可以通过调用地方通过定义找到这个方式,那么这点给我们的启示就是桥接的类都可以这么调用,包括Array、Set、Dictionary

目测用好Swift还要懂OC。。。

带你走入正确遍历所有字符的姿势

Swift中的String是UTF-16编码, 也就是16位的unichar字符的序列。但是, 我们平常书写的字符, 并不全部都是用唯一的一个16位字符来表示, 而是有一部分用两个16位字符来表示, 这就是surrogate pairs的概念. 如果还是用上面的方法遍历字符串, 就会出现”断字”. 例如图中这个Apple Color Emoji的”THUMBS UP SIGN”字符, 其实是用2个16位unichar来表示, 它的Unicode是U+1F44D, 用(U+D83D U+DC4D)两个字符来表示.

这就涉及到的字符序列的一个概念,即用多少个字符来表示你所看到的字符视图,具体实现依靠rangeOfComposedCharacterSequencesForRange和rangeOfComposedCharacterSequenceAtIndex来解决

NSRange range;
for(int i=0; i<str.length; i+=range.length){
    range = [str rangeOfComposedCharacterSequenceAtIndex:i];
    NSString *s = [str attributedSubstringFromRange:range];
}
一次遍历一个子串, 而不是遍历一个unichar了

说到字符这里,顺带提一下,Swift中String是分字符视图的,也就是说你可以以unicode view也可以utf8 view来对其进行描述,查看相关的内容,比如

 let x = self.unicodeScalars.count

提到count我们再说下获取count的方法,一般字符都是unicode,这里不再对特殊字符进行说明,获取count也就是NSString中对应的length,方式是string.characters.count,也可以是string.count而这是等同的,不同的是charaters默认是unicode编码,如果用别的编码可以更换中间部分,比如改用utf8String

NS中的not found对应的是Swift中的可选值

  • 如果有可选值,那么当返回失败的时候应该考虑nil而不是isEmpty等属性,这点也是让我们提升对可选值的理解

  • 函数返回值上+!,表示的是应该返回?但是函数自动帮我们解绑

再说一些额外的方法

Swift与Char之间的关系

  • appendingFormat不对原String进行修改
  • subString 方法已被弃用,现在使用的是下标脚本,同时返回的是String.subString不是String

Swift与C语言的爱恨情仇

这个地方是我遇到最难受的地方了,因为有些函数你遇到会要求你使用UnsafePointer之类的东东,而这些东西其实就是C语言的东西,下面我们先简单说下UnsafePointer相关的两个常用类

UnsafeRawPointer

原始指针,用于访问非指定类型的数据。它不提供自动的内存管理、内存对齐、类型安全,这些都需要你自己来做同时你需要管理它的声明周期,避免野指针和内存泄漏

指针的内存状态

  • 分配空间未绑定类型未初始化 bindMemory绑定类型不初始化
  • 绑定类型未初始化 UnsafePointer UnsafeMutablePointer 可以访问,重新绑定类型需要先deinitialized在绑定,除非是trival 类型
  • 绑定类型初始化
  • 回收内存deallocated,指针指向未分配的内存

Raw Pointer 是byte级别的,+ - 都是指针的偏移

UnsafeRawPointer
作为函数的时候可以隐式转换

  • UnsafeRawPointer
  • SafePointer
  • Swift基本类型 采用inout语法即地址&
  • 对象类型指针 采用inout语法即地址&

不做函数的时候需要显示桥接

var number = 5
let numberPointer = UnsafeRawPointer(&number)

此处是实践MD5加密的写法

let chars = self.components(separatedBy: "")
       let results:[UInt8] = Array.init(repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
       let result = UnsafeMutablePointer(mutating: results)
       CC_MD5(UnsafeRawPointer(chars),(CC_LONG)(chars.count),result)    
这里顺带说一下如何在swift工程中导入CommonCrypto

talk is cheap, show me the code

#import <CommonCrypto/CommonCrypto.h>
放入OC Swift混编的.h文件中即可

UnsafePointer

用于访问固定类型的指针

访问元素

  • pointee

  • 下标访问

  • 也可以不绑定类型继续访问元素,只要目标类型与当前类型可以转换

    • 转换到UnsafeRawPointer之后再load即可
  • 绑定到其他类型

  • 临时绑定到不同类型

    • withMemoryRebound
  • 永久绑定到不同类型

    • UnsafeRawPointer(uint64Pointer)
      .bindMemory(to: UInt64.self, capacity: 1)
    • 绑定之后原类型的访问就是未定义的

只作为函数的时候可以隐式的转换,与Raw不同没有基本类型的构造函数

神秘的UnsafePointer究竟是什么

这里我们通过代码来说明,UnsafePointer<CChar>其实就是[CChar],但是这个不同于ContiguousArray<CChar> --> 元身是string.utf8String

let charArray: [CChar] = str.cString(using: String.Encoding.utf8)!
        let m = str.utf8CString
        let x = String.init(cString: charArray)//要求是UnsafePointer类型
         let y = String.init(cString: m)//报错

Swift与C语言理解

Swift中经常会涉及到C语言的东西,这个时候一般要求的就是UnsafePointer巴拉巴拉之类的,比如Data.append()方法要求传入UnsafePointer<UInt8>类型的数据,我们可以选择通过UnsafeRawPointer生成UnsafePonter(这个笨笨的方式我从API找了半天),当然也可以直接使用类似C的结构如下

let count = 1
//第一种方式UnsafeMutableRawPointer.allocate构造,记得deallocate
let bytesPointer = UnsafeMutableRawPointer.allocate(bytes: count * MemoryLayout<UInt8>.stride,alignedTo: MemoryLayout<UInt8>.alignment)
let int8Pointer = bytesPointer.initializeMemory(as: UInt8.self,  count: count, to: outbuf[i])
theData.append(bytesPointer.assumingMemoryBound(to: UInt8.self), count: 1)  
//第二种方式使用类似C的方法[UInt8](repeating:outbuf[i],count:1),其实这是Array的方法。。囧
theData.append([UInt8](repeating:outbuf[i],count:1), count: 1)
theData.append(Array.init(repeating:outbuf[i],count:1), count: 1)
 // After using 'int8Pointer':
int8Pointer.deallocate(capacity: count)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容