天天看點

swift c語言 字元串,你真的懂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

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

放入OC Swift混編的.h檔案中即可

UnsafePointer

用于通路固定類型的指針

通路元素

pointee

下标通路

也可以不綁定類型繼續通路元素,隻要目标類型與目前類型可以轉換

轉換到UnsafeRawPointer之後再load即可

綁定到其他類型

臨時綁定到不同類型

withMemoryRebound

永久綁定到不同類型

UnsafeRawPointer(uint64Pointer)

.bindMemory(to: UInt64.self, capacity: 1)

綁定之後原類型的通路就是未定義的

隻作為函數的時候可以隐式的轉換,與Raw不同沒有基本類型的構造函數

神秘的UnsafePointer究竟是什麼

這裡我們通過代碼來說明,UnsafePointer其實就是[CChar],但是這個不同于ContiguousArray --> 元身是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類型的資料,我們可以選擇通過UnsafeRawPointer生成UnsafePonter(這個笨笨的方式我從API找了半天),當然也可以直接使用類似C的結構如下

let count = 1

//第一種方式UnsafeMutableRawPointer.allocate構造,記得deallocate

let bytesPointer = UnsafeMutableRawPointer.allocate(bytes: count * MemoryLayout.stride,alignedTo: MemoryLayout.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)