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