主頁 > 移動端開發 > Swift 進階(三)列舉

Swift 進階(三)列舉

2021-04-02 06:48:12 移動端開發

列舉的基本用法

enum Direction {
    case north
    case south
    case east
    case west
}

// 簡便寫法
enum Direction {
    case north, south, east, west
}

var dir = Direction.west
dir = Direction.east
dir = .north

switch dir {
case .north:
    print("north")
case .south:
    print("south")
case .east:
    print("east")
case .west:
    print("west")
}

關聯值(Associated Values)

有時會將列舉的成員值其他型別的值關聯存盤在一起,會非常有用

enum Score {
	case points(Int)
	case grade(Character)
}

var score = Score.points(96)
score = .grade("A")

switch score {
case let .points(i):
  debugPrint(i)
case let .grade(i):
  debugPrint(i)
}
enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}

var date = Date.digit(year: 2020, month: 12, day: 5)
date = .string("2020-12-5")
   
switch date {
case .digit(let year, let month, let day):
  debugPrint(year, month, day)
case let .string(value):
  debugPrint(value)
}
enum Password {
    case number(Int, Int, Int, Int)
    case gesture(String)
}

var pwd = Password.number(5, 6, 4, 7)
pwd = .gesture("12369")

switch pwd {
case let .number(n1, n2, n3, n4):
    print("number is ", n1, n2, n3, n4)
case let .gesture(str):
    print("gesture is ", str)
}

必要時let可以改成var

原始值(Raw Values)

列舉成員可以使用相同型別的默認值預先關聯,這個默認值叫做原始值

enum PokerSuit: String {
    case spade = "1"
    case heart = "2"
    case diamond = "3"
    case club = "4"
}

let suit = PokerSuit.heart
debugPrint(suit)
debugPrint(suit.rawValue)
debugPrint(PokerSuit.spade.rawValue)

原始值不占用列舉變數的記憶體,原始值只是關聯上了列舉變數,所以原始值占用記憶體的大小并不是列舉變數的大小

隱式原始值(Implicitly Assigned Raw Values)

如果列舉的原始值型別是Int、String,Swift會自動分配原始值

字串默認分配的原始值就是其變數名

enum Direction: String {
    case north = "north"
    case south = "south"
    case east = "east"
    case west = "west"
}

// 等價于上面
enum Direction: String {
	case north, south, east, west
}

debugPrint(Direction.north.rawValue) // north

Int型別默認分配的原始值是從0開始遞增的數字

enum Season: Int {
    case spring, summer, autumn, winter
}

print(Season.spring.rawValue) // 0
print(Season.summer.rawValue) // 1
print(Season.autumn.rawValue) // 2
print(Season.winter.rawValue) // 3

如果有指定原始值的,下一個就會按照已經指定的值遞增分配

enum Season: Int {
    case spring = 1, summer, autumn = 4, winter
}

print(Season.spring.rawValue) // 1
print(Season.summer.rawValue) // 2
print(Season.autumn.rawValue) // 4
print(Season.winter.rawValue) // 5

遞回列舉(Recursive Enumeration)

遞回列舉要用indirect關鍵字來修飾enum,不然會報錯

indirect enum ArithExpr {
    case number(Int)
    case sum(ArithExpr, ArithExpr)
    case difference(ArithExpr, ArithExpr)
}

或者

enum ArithExpr {
	case number(Int)
	indirect case sum(ArithExpr, ArithExpr)
	indirect case difference(ArithExpr, ArithExpr)
}

let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let sum = ArithExpr.sum(five, four)

func calculate(_ expr: ArithExpr) -> Int {
    switch expr {
    case let .number(value):
        return value
    case let .sum(left, right):
        return calculate(left) + calculate(right)
    case let .difference(left, right):
        return calculate(left) - calculate(right)
    }
}

calculate(difference)

MemoryLayout

可以使用MemoryLayout獲取資料型別占用的記憶體大小

64bit的Int型別8個位元組

let age = 10

MemoryLayout<Int>.stride // 8, 分配占用的空間大小
MemoryLayout<Int>.size // 8, 實際用到的空間大小
MemoryLayout<Int>.alignment // 8, 對齊引數

等同于

MemoryLayout.size(ofValue: age)
MemoryLayout.stride(ofValue: age)
MemoryLayout.alignment(ofValue: age)

關聯值和原始值的區別:關聯值型別會存盤到列舉變數里面,原始值因為一開始就會知道默認值是多少,所以只做記錄,不會存盤

enum Password {
	case number(Int, Int, Int, Int)
	case other
}

MemoryLayout<Password>.stride // 40,分配占用的空間大小
MemoryLayout<Password>.size // 33,實際用到的空間大小
MemoryLayout<Password>.alignment // 8,對齊引數
enum Session: Int {
	case spring, summer, autnmn, winter
}

MemoryLayout<Session>.stride // 1,分配占用的空間大小
MemoryLayout<Session>.size // 1,實際用到的空間大小
MemoryLayout<Session>.alignment // 1,對齊引數

列舉變數的記憶體布局

我們知道通過MemoryLayout可以獲取到列舉占用記憶體的大小,那列舉變數分別占用多少記憶體呢?

要想知道列舉變數的大小,我們需要通過查看列舉變數的記憶體布局來進行分析

列舉變數的分析準備

我們可以需要通過Xcode里的View Memory選項來查看詳細的記憶體布局

1.可以在運行程式時,通過控制臺列印的列舉變數右鍵選擇View Memory of *進入到記憶體布局的頁面

-w440

2.還可以從Xcode標題欄中選擇Debug -> Debug Workflow -> View Memory進入到記憶體布局的頁面

-w569

3.進入到該頁面,然后通過輸入變數的記憶體地址來查看

-w1129

4.我們可以下載一個小工具來獲取到變數的記憶體地址

下載地址:https://github.com/CoderMJLee/Mems

5.然后將下載好的這個檔案Mems.swift拖到自己的Xcode

呼叫這個函式就可以了

print(Mems.ptr(ofVal: &t))

我們來分析下面的列舉變數的情況

enum TestEnum {
    case test1, test2, test3
}

var t = TestEnum.test1
print(Mems.ptr(ofVal: &t))

t = TestEnum.test2
t = TestEnum.test3

print(MemoryLayout<TestEnum>.stride) // 1
print(MemoryLayout<TestEnum>.size) // 1
print(MemoryLayout<TestEnum>.alignment) // 1

分別將斷點打在給列舉變數t賦值的三句代碼上,然后運行程式觀察每次斷點之后的記憶體布局有什么變化

-w1127

-w1124

-w1124

通過上圖可以看到,每個列舉變數都分配了一個位元組的大小,并且存盤的值分別是0、1、2,我們可以知道這一個位元組的大小就是用來存盤列舉成員值

我們再來分析一個列舉變數的情況

enum TestEnum: Int {
    case test1 = 1, test2 = 2, test3 = 3
}

var t = TestEnum.test1
print(Mems.ptr(ofVal: &t))

t = TestEnum.test2
t = TestEnum.test3

print(MemoryLayout<TestEnum>.stride) // 1
print(MemoryLayout<TestEnum>.size) // 1
print(MemoryLayout<TestEnum>.alignment) // 1

-w1131

-w1126

-w1125

通過上圖可以看到,每個列舉變數存盤的值也是0、1、2,并且分配了一個位元組的大小

可以證明列舉變數的記憶體大小和原始值型別無關,而且列舉變數里存盤的值和原始值也無關

我們再來分析一個列舉變數的情況

enum TestEnum {
    case test1(Int, Int, Int) // 24
    case test2(Int, Int) // 16
    case test3(Int) // 8
    case test4(Bool) // 1
    case test5 // 1
}

var t = TestEnum.test1(1, 2, 3)
print(Mems.ptr(ofVal: &t))

t = TestEnum.test2(4, 5)
t = TestEnum.test3(6)
t = TestEnum.test4(true)
t = TestEnum.test5

MemoryLayout<TestEnum>.size // 25
MemoryLayout<TestEnum>.stride // 32
MemoryLayout<TestEnum>.alignment // 8

我們先通過列印了解到列舉型別總共分配了32個位元組,然后我們通過斷點分別來觀察列舉變數的記憶體布局

-w773
-w1124

執行完第一句我們可以看到,前面24個位元組分別用來存盤關聯值1、2、3,第25個位元組用來存盤成員值0,之所以分配32個位元組是因為記憶體對齊的原因

// 調整排版后的記憶體布局如下所示
01 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

-w719
-w1193

執行完第二句我們可以看到,前面16個位元組分半用來存盤關聯值4、5,然后第25個位元組用來存盤成員值1

// 調整排版后的記憶體布局如下所示
04 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00

-w563
-w1196

執行完第三句我們可以看到,前面8個位元組分半用來存盤關聯值6,然后第25個位元組用來存盤成員值2

// 調整排版后的記憶體布局如下所示
06 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00

-w665
-w1192

執行完第四句我們可以看到,由于是Bool型別,那么只用了第一個位元組來存盤關聯值1,然后第25個位元組用來存盤成員值3

// 調整排版后的記憶體布局如下所示
01 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00

-w676
-w1191

執行完最后一句我們可以看到,由于沒有關聯值,那么只用了第25個位元組存盤成員值4

// 調整排版后的記憶體布局如下所示
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
04 00 00 00 00 00 00 00

總結:記憶體分配情況:一個位元組存盤成員值,n個位元組存盤關聯值(n取占用記憶體最大的關聯值),任何一個case的關聯值都共有這n個位元組

我們再來看幾個情況

enum TestEnum {
    case test
}

MemoryLayout<Session>.stride // 1,分配占用的空間大小
MemoryLayout<Session>.size // 0,實際用到的空間大小
MemoryLayout<Session>.alignment // 1,對齊引數

如果列舉里只有一個case,那么實際用到的空間為0,都不用特別分配記憶體來進行存盤

enum TestEnum {
    case test(Int)
}

MemoryLayout<Session>.stride // 8,分配占用的空間大小
MemoryLayout<Session>.size // 8,實際用到的空間大小
MemoryLayout<Session>.alignment // 8,對齊引數

可以看到分配的記憶體大小就是關聯值型別決定的,因為只有一個case,所以都不需要再額外分配記憶體來存盤是哪個case了

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/271194.html

標籤:iOS

上一篇:iOS-審核4.3入坑(已出坑)

下一篇:Swift 進階(四)結構體和類

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more