Swift可選值優(yōu)化示例詳解
nil 的語義
在 Objective-C 中,nil 表示空對象,它本質(zhì)是一個(gè)指向 0x00000000 的指針。但對于非指針的值類型,OC 中是無法表示_沒有值_這個(gè)概念的,比如 NSInteger,它可以是 0,也可以是其他任何值,但就是不存在_沒有值_。
Swift 作為一種強(qiáng)類型的語言,它從一開始就引入了_沒有值_這個(gè)概念,雖然還是用 nil 關(guān)鍵字,但實(shí)際語義上有所不同。比如 Int?,它可以是 nil,也可以是 0,0 是一個(gè)具體的值,而 nil 不是。然而,計(jì)算機(jī)作為一個(gè)二進(jìn)制的機(jī)器,它內(nèi)存中保存的非 0 即 1,如何表示_沒有值_呢?換句話說,nil 在內(nèi)存中究竟是什么?我們可以通過簡單的代碼找出它在內(nèi)存中的真相。
nil 在內(nèi)存中的表示
/// 以下方法取 value 的地址,并從地址處向后取它在內(nèi)存中的大小 size 個(gè)字節(jié),轉(zhuǎn)為對應(yīng)的數(shù)組
func bytes<T>(of value: T) -> [UInt8] {
var value = value
let size = MemoryLayout<T>.size
return withUnsafePointer(to: &value, {
$0.withMemoryRebound(
to: UInt8.self,
capacity: size,
{
Array(UnsafeBufferPointer(
start: $0, count: size))
})
})
}
var int: Int? = 0
bytes(of: int) // [0, 0, 0, 0, 0, 0, 0, 0, 0]
int = nil
bytes(of: int) // [0, 0, 0, 0, 0, 0, 0, 0, 1]從上面我們可以得知,可選的 Int? 類型比普通 Int 類型多占一個(gè)字節(jié),用來表示是不是 沒有值。如果這樣的話,在 struct 或 class 中用可選類型豈不是會(huì)浪費(fèi)較多內(nèi)存空間?因?yàn)閮?nèi)存對齊的緣故,多一個(gè)字節(jié),就要浪費(fèi)剩下的 7 字節(jié),比如:
struct N {
var b: Int? = 2
var a: Int? = 3
}
var n = N()
bytes(of: n) // [2, 0, 0, 0, 0, 0, 0, 0, 0, 76, 68, 3, 1, 0, 0, 0,
// 3, 0, 0, 0, 0, 0, 0, 0, 0]以上原本可以用 16 字節(jié)表示的結(jié)構(gòu)體,實(shí)際上占了 25 字節(jié)(考慮結(jié)尾處內(nèi)存對齊,其實(shí)占了 32 字節(jié))。我們在實(shí)際開發(fā)中,可能會(huì)在 class 中聲明大量的可選字段,如果都這樣的話,那內(nèi)存使用率也太低了,有優(yōu)化手段嗎?
答案是有的,而且 Swift 編譯器已經(jīng)默默幫我們做了。
nil 的優(yōu)化
Bool
Bool 類型理論上只用 0 1 兩個(gè)值,一個(gè) bit 即可,但它卻占了一整個(gè) byte ,剩下的幾個(gè) bit 是可以用來區(qū)分是否有值的。
var b: Bool? = false bytes(of: b) // [0] b = true bytes(of: b) // [1] b = nil bytes(of: b) // [2]
從以上結(jié)果得知,Swift 用 2 表示 Bool? 的_沒有值_,所以沒有內(nèi)存浪費(fèi)。這樣也使得 Bool? 不再是兩態(tài)的開關(guān),而是一個(gè)三態(tài)的開關(guān)。于是經(jīng)常在代碼中看到看起來比較蠢的寫法:
var value: Bool?
if value == true {
}因?yàn)橐话銇碚f是不建議 Bool 值與 true 判斷等的,它本身已經(jīng)是 Bool 了。而在 Swift 中又用起來是那么自然……
String
String 類型不同于 Int 這種——0 也是合法值,String 的內(nèi)存值為 0 是可以表示_沒有值_的,所以它也沒有內(nèi)存浪費(fèi)
String 在 Swift 中是一個(gè)結(jié)構(gòu)體,無論字符串多長,String 變量本身只占 16 字節(jié),短的字符串通過類似 OC 中 Tagged Pointer 的技術(shù)直接存在指針中,長的字符串需要指向堆內(nèi)存地址。
Class
Class 類型同 OC 中的一樣,是指針類型,空指針可以表示_沒有值_,沒有內(nèi)存浪費(fèi)。
class MyObject {
var b: Int? = 2
var a: Int? = 3
}
var o: MyObject? = .init()
bytes(of: o) // [160, 142, 188, 2, 0, 96, 0, 0]
o = nil
bytes(of: o) // [0, 0, 0, 0, 0, 0, 0, 0]無論 Class 中有多少成員變量,Class 變量本身(即指向它的指針)只占 8 字節(jié)(64位系統(tǒng)中)。
Enum
枚舉類型一般是有限的,最終總可以找到一個(gè)不在枚舉范圍內(nèi)的值表示 _沒有值_,也可以沒有內(nèi)存浪費(fèi)。
enum Edge {
case left
case right
case top
case bottom
}
var e: Edge? = .left
bytes(of: e) // [0]
e = .bottom
bytes(of: e) // [3]
e = nil
bytes(of: e) // [4],用越界值表示 nil,沒有值當(dāng)然并不是所有 Enum 類型都能這樣,帶關(guān)聯(lián)值的就可能不行。
結(jié)語
綜上所述,Swift 編譯器會(huì)盡可能地優(yōu)化可選值的內(nèi)存占用,日常開發(fā)并不需要太多關(guān)心,但是部分情況仍要求開發(fā)者盡量少使用可選值,如結(jié)構(gòu)體中連續(xù)幾個(gè)可選 Int 的情況,如果 0 也能滿足代碼邏輯,就使用非可選值,并用 0 初始化它吧!
// 浪費(fèi)的內(nèi)存比較可觀
struct My {
var a: Int?
var b: Int?
var c: Int?
var d: Int?
}以上就是Swift可選值優(yōu)化示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Swift可選值優(yōu)化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Swift實(shí)現(xiàn)監(jiān)聽鍵盤通知及一些處理詳解
這篇文章主要給大家介紹了關(guān)于Swift實(shí)現(xiàn)監(jiān)聽鍵盤通知及一些處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
Swift利用AFN實(shí)現(xiàn)封裝網(wǎng)絡(luò)請求詳解
網(wǎng)絡(luò)請求工具是我們經(jīng)常用到的工具類,所以下面這篇文章主要給大家介紹了關(guān)于Swift利用AFN如何實(shí)現(xiàn)封裝網(wǎng)絡(luò)請求的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
EvenLoop模型在iOS的RunLoop應(yīng)用示例
這篇文章主要為大家介紹了EvenLoop模型在iOS的RunLoop應(yīng)用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Swift的開發(fā)環(huán)境搭建以及基本語法詳解
這篇文章主要介紹了Swift的開發(fā)環(huán)境搭建以及基本語法詳解,是Swift入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11

