Swift踩坑實(shí)戰(zhàn)之一個(gè)字符引發(fā)的Crash
最近因?yàn)橐粋€(gè)字符引發(fā)了 Crash,因?yàn)閷?shí)際的業(yè)務(wù)場景不便描述,這里便用一段測試代碼作說明。
話不多說,直接上代碼:
let testCharacters: Set<Character> = ["!", "\"", "$", "%", "&", "'", "+", ",", "<", "=", ">", "@", "[", "]", "`", "{", "}"]
let testString = "@`Hello World?!"
var result: UInt8 = 0
for character in testString {
if testCharacters.contains(character) {
result += character.asciiValue!
}
}上面的代碼做的事情是:取出 testString 里特定字符的 ASCII 碼,然后相加。
我們來 Review 下這段代碼,有經(jīng)驗(yàn)的同學(xué)應(yīng)該立馬嗅到了代碼里的壞味道:character.asciiValue! 這里用了強(qiáng)解。
那這里的強(qiáng)解用得合理嗎?因?yàn)槎x在 testCharacters 里的字符肯定都有對應(yīng)的 ASCII 碼,咋一看這里用強(qiáng)解也沒關(guān)系。
但是,如果我們實(shí)際跑一下,就會(huì)出現(xiàn)因?yàn)?asciiValue 為 nil 的強(qiáng)解 Crash 了。這是為什么呢?
關(guān)鍵在于 testString 里面包含了 全角字符。testString 里的后一個(gè) ? 是一個(gè)全角字符,它是沒有 asciiValue 的。
我們可以在 Swift Playgrounds 里執(zhí)行下面的代碼得到答案:
let halfWidth = "`" halfWidth.lengthOfBytes(using: .utf8) // 1 halfWidth.first!.isASCII // true halfWidth.first!.asciiValue // 96 let fullWidth = "?" fullWidth.lengthOfBytes(using: .utf8) // 3 fullWidth.first!.isASCII // false fullWidth.first!.asciiValue // nil // Character 實(shí)現(xiàn) Equatable 協(xié)議,判斷出兩個(gè)值是相等的。 halfWidth == fullWidth // true
從上面代碼執(zhí)行結(jié)果可以看到,halfWidth 這個(gè)半角字符占一個(gè)字節(jié)長度,對應(yīng)的 ASCII 碼為 96 而全角字符 fullWidth 占三個(gè)字節(jié)長度,其 asciiValue 為空的。
Swift 數(shù)組的 contains 方法利用的是 Equatable 協(xié)議 , 從上面代碼里 halfWidth == fullWidth 的結(jié)果為 true 來看,Character 實(shí)現(xiàn)的 Equatable 協(xié)議并沒有考慮字符全角/半角的情況。
用肉眼看,完全看不出字符有何不同,而 contains 方法結(jié)果為 true 也影響了我們的判斷,以為這個(gè)強(qiáng)解是 OK 的,稍不注意就導(dǎo)致了 Crash。
最后,從維基百科上整理了關(guān)于全角/半角的歷史知識(shí):
在早期的計(jì)算機(jī)中,英語或拉丁字母語言使用的系統(tǒng),每一個(gè)字母或符號(hào),都是使用一字節(jié)的空間(一字節(jié)由 8 比特組成,共256個(gè)編碼空間)來儲(chǔ)存;而漢語、日語及韓語文字,由于數(shù)量大大超過256個(gè),故慣常使用兩字節(jié)來儲(chǔ)存一個(gè)字符。所以這原本是編碼層面的“單字節(jié)”“雙字節(jié)”的問題。
當(dāng)時(shí)的電腦使用等寬字體(如DOS、部分文字編輯器等)時(shí),字體也就順應(yīng)這種編碼形式,將中日韓文字的寬度繪制成拉丁字母和數(shù)字的兩倍,這樣字符的編碼存儲(chǔ)和顯示寬度可以一一對應(yīng)起來:
- 單字節(jié)文字 顯示成 半寬,
- 雙字節(jié)文字 顯示成 全寬。
因此當(dāng)時(shí)的用戶就開始習(xí)慣稱中、日、韓等文字為 全角字符,而稱拉丁字母或數(shù)字為 半角字符。
但是,后來計(jì)算機(jī)的文字編碼技術(shù)已經(jīng)發(fā)生很大變化,存儲(chǔ)一個(gè)字符可能用一個(gè)、兩個(gè)、四個(gè)或者更多的字節(jié)。一個(gè)英文字符即使顯示為半寬,依照不同的編碼方式,并不一定是用一個(gè)字節(jié)存儲(chǔ)。
因此,現(xiàn)在字符編碼存儲(chǔ)和字符顯示寬度的已經(jīng)沒有一一對應(yīng)關(guān)系。
但是由于字符編碼和字形寬度曾經(jīng)的對應(yīng)關(guān)系,很多用戶一直習(xí)慣性地使用"全角/半角"詞匯。
因此現(xiàn)在的 全角字 可能是指:
- 用兩個(gè)字節(jié)存儲(chǔ)的字符
- ASCII(所謂半角英文和數(shù)字)以外所有的字符
- 顯示上字身寬度為一比一正方形的字形。
總結(jié)
到此這篇關(guān)于Swift踩坑實(shí)戰(zhàn)之一個(gè)字符引發(fā)Crash的文章就介紹到這了,更多相關(guān)Swift字符引發(fā)的Crash內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
switch循環(huán)所支持的數(shù)據(jù)類型案例分析
這篇文章主要介紹了switch循環(huán)所支持的數(shù)據(jù)類型,本文通過實(shí)際案例講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
swift4 使用DrawerController實(shí)現(xiàn)側(cè)滑菜單功能的示例代碼
這篇文章主要介紹了swift4 使用DrawerController實(shí)現(xiàn)側(cè)滑功能的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
Swift實(shí)現(xiàn)倒計(jì)時(shí)5秒功能
這篇文章主要為大家詳細(xì)介紹了Swift實(shí)現(xiàn)倒計(jì)時(shí)5秒功能,在“登錄”和“注冊”頁面也有相似功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
Swift代碼實(shí)現(xiàn)冒泡排序算法的簡單實(shí)例
冒牌排序可謂最基本的排序算法之一,穩(wěn)定而沒有優(yōu)化空間:D 下面就一起來看一下Swift代碼實(shí)現(xiàn)冒泡排序算法的簡單實(shí)例:2016-06-06
SwiftUI中@ViewBuilder的相關(guān)知識(shí)點(diǎn)解密
IOS開發(fā)目前最主流的框架當(dāng)屬SwiftUI了,這篇文章主要給大家介紹了關(guān)于SwiftUI中@ViewBuilder的一些相關(guān)知識(shí)點(diǎn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-07-07
Swift 3.0將UILabel數(shù)字顏色設(shè)置為紅色的方法
這篇文章主要介紹了關(guān)于在Swift中將UILabel數(shù)字顏色設(shè)置為紅色的方法,文中給出了詳細(xì)的示例代碼,相信對大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。2017-03-03

