一玉凯、流程控制
-
1.1势腮、if 語(yǔ)句
let age = 5 if age > 10 { print("\(age) 大于 10 ") } else if age > 3 { print("\(age) 大于 3 ") } else { print("\(age) 小于 3 ") }
提示:
- if 后面的條件可以省略小括號(hào),而條件后面的
花括號(hào)
不可以省略 -
if 后面的類型只能是 bool 類型
if 后面的類型只能是 bool 類型
- if 后面的條件可以省略小括號(hào),而條件后面的
-
1.2漫仆、while 語(yǔ)句(先判斷后執(zhí)行)
var num = 5 while num > 0 { num -= 1 print("num=\(num)") }
打印結(jié)果:num=4捎拯、num=3、num=2盲厌、num=1署照、num=0
-
1.3、repeat while 語(yǔ)句(先執(zhí)行后判斷)
var num = -1 repeat{ print("num=\(num)") }while num > 0
打印結(jié)果:num=-1吗浩,
提示:repeat-while 相當(dāng)于 OC 里面的 do-while
-
1.4建芙、for 語(yǔ)句
-
閉區(qū)間運(yùn)算符:
a...b
;a<= 取值 <=b
let names = ["小1","小2","小3","小4"] for i in 0...3 { print("name=\(names[i])") }
提示:上面的
0...3
可以替換為let range = 0...3
let a = 0 var b = 3 let names = ["小1","小2","小3","小4"] for i in a...b { print("name=\(names[i])") }
提示:區(qū)間可以用常量或者變量來表示的
for _ in 0...3 { print("??") }
打印了 四次 ??
-
for 循環(huán)里面的 i 默認(rèn)是 let拓萌,有需要的時(shí)候我們可以聲明為 var,如下
for var i in 1...3 { i += 2 print("i=\(i)") }
打印結(jié)果:3岁钓、4、5
-
半開區(qū)間運(yùn)算符:
a..<b
微王;a <= 取值 < b
for i in 1..<3 { print("i=\(i)") }
打印結(jié)果:1屡限、2
-
-
1.5、for - 區(qū)間運(yùn)算符用在數(shù)組上
-
例一
let names = ["小1","小2","小3","小4"] for name in names[0...3] { print("name=\(name)") }
打印結(jié)果:小1炕倘、小2钧大、小3、小4
-
例二:?jiǎn)蝹?cè)區(qū)間:讓區(qū)間朝一個(gè)方向盡可能的遠(yuǎn)
let names = ["小1","小2","小3","小4"] for name in names[1...] { print("name=\(name)") }
打印結(jié)果:小2罩旋、小3啊央、小4
let names = ["小1","小2","小3","小4"] for name in names[...2] { print("name=\(name)") }
打印結(jié)果:小1、小2涨醋、小3
let names = ["小1","小2","小3","小4"] for name in names[..<2] { print("name=\(name)") }
打印結(jié)果:小1瓜饥、小2
let range = ...5 print(range.contains(8)) print(range.contains(3)) print(range.contains(-1))
打印結(jié)果:false、true浴骂、true
提示: let range = ...5 代表 range的范圍從無窮小到 5乓土,包含5
-
-
1.6、區(qū)間類型(重點(diǎn))
let range1:ClosedRange<Int> = 1...3 let range2:Range<Int> = 1..<3 let range3:PartialRangeThrough<Int> = ...5
-
字符溯警、字符串也能進(jìn)行區(qū)間運(yùn)算趣苏,但默認(rèn)不能在
for-in
中,因?yàn)?ClosedRange:不可數(shù)的閉區(qū)間let stringRange1 = "cc"..."ff" // ClosedRange<String> stringRange1.contains("cb") // false stringRange1.contains("dz") // true stringRange1.contains("fg") // false let stringRange2 = "a"..."f" // ClosedRange<String> stringRange2.contains("d") // true stringRange2.contains("n") // false
提示:比較的是ascii梯轻,第1位比較第1位食磕,第2位比較第2位,如果第1位大于第1位就沒必要比較第二位了喳挑,依次類推
Ascii字符表 -
0到~
包含了所有可能要用到的 ASCII 字符let characterRange:ClosedRange<Character> = "\0"..."~" characterRange.contains("b") // true
-
-
1.8彬伦、帶間隔的區(qū)間值
let hours = 12 let hourInterval = 2 // 從tickMark 的取值:從 4 開始,累加2伊诵,不超過 12 for tickMark in stride(from: 4, to: hours, by: hourInterval) { print("tickMark=\(tickMark)") }
打印結(jié)果:4媚朦、6、8日戈、10
-
1.9询张、switch 語(yǔ)句
-
案例一:switch的基本使用
var num = 10 switch num { case 1: print("值是1") break case 2: print("值是2") break case 3: print("值是3") break default: print("沒有匹配") break }
提示:case 和 default 后面不能寫大括號(hào)
{}
,默認(rèn)可以不寫 break浙炼,并不會(huì)貫穿到后面的條件 -
案例二:使用 fallthrough 可以實(shí)現(xiàn)貫穿效果
var num = 1 switch num { case 1: print("num 值是 1") fallthrough case 2: print("num 值是 2") case 3: print("num 值是 3") default: print("沒有匹配") }
打印結(jié)果:num 值是 1份氧、num 值是 2
-
switch使用注意點(diǎn):
-
<1>、switch 必須能保證處理所有的情況
switch 必須能保證處理所有的情況 -
<2>弯屈、case 或者 default 后面至少有一條語(yǔ)句蜗帜,如果不想有語(yǔ)句可以寫 break
var num = 1 switch num { case 1: print("num 值是 1") case 2: break default: print("沒有匹配") }
-
<3>、如果能保證處理所有的情況资厉,也可以不必使用 default,如下
enum Answer {case right,wrong} let answer = Answer.right switch answer { case Answer.right: print("right") case Answer.wrong: print("wrong") }
提示:上面已經(jīng)確定 answer 是 Answer 類型厅缺,因此可以省略 Answer
enum Answer {case right,wrong} let answer = Answer.right switch answer { case .right: print("right") case .wrong: print("wrong") }
-
-
復(fù)合條件:switch也支持Character、String 類型,如下例子
-
例一:String 類型
let name = "marry" switch name { case "Jack": print("Jack") case "marry": print("marry") default: print("找不到某人") }
-
例二:String 類型
let name = "marry" switch name { case "Jack","marry": print("Jack and marry") default: print("找不到某人") }
-
例三:Character 類型
let character = "A" switch character { case "a","A": print("字母 a或者A") default: print("找不到字母") }
-
-
匹配區(qū)間和元組匹配:
-
匹配區(qū)間
let count = 20 switch count { case 0: print("none") case 1..<5: print("a few") case 5..<100: print("several") case 100..<1000: print("hundreds of") default: print("many") }
-
元組匹配
let point = (1,1) switch point { case (0,0): print("the origin") case (_,0): print("on the x-axis") case (0,_): print("on the y-axis") case (-2...2,-2...2): print("inside the box") default: print("many") }
提示:可以使用
_
忽略某個(gè)值
-
-
值綁定:必要時(shí)湘捎,let 可以改為 var
let point = (2,0) switch point { case (let x,0): print("on the x-axis with an x value of \(x)") case (0,let y): print("on the y-axis with an y value of \(y)") case let (x,y): print("somehere else at (\(x),\(y))") }
-
-
1.10诀豁、where語(yǔ)句
let point = (2,-2) switch point { case let (x,y) where x == y: print("on the line x == y") case let (x,y) where x == -y: print("on the line x == -y") case let (x,y): print("(\(x),\(y)) is just some arbitrary point") }
例二:所有的正數(shù)相加
let numbers = [-1,2,3,-10,20] var sum = 0 for num in numbers where num > 0 { sum += num } print("最后正數(shù)的和=\(sum)")
提示:where 用來過濾 負(fù)數(shù)
-
1.11、標(biāo)簽語(yǔ)句
outer: for i in 1...4{ for k in 1...4 { if k == 3 { continue outer } if i == 3 { break outer } print("i=\(i) k=\(k)") } }
二窥妇、函數(shù)
-
2.1舷胜、函數(shù)的定義:是一組代碼的組合,因此函數(shù)也被稱為完成特殊功能的代碼塊活翩。函數(shù)三要素:函數(shù)名烹骨、參數(shù)、返回值材泄。
-
有返回值沮焕,有參數(shù)
func pi()->Double{ return 3.14 } print(pi()) func sum(num1:Int,num2:Int)->Int{ return num1 + num2 } print(sum(num1: 1, num2: 3))
提示:形參默認(rèn)是 let,也只能是 let
-
無返回值拉宗,無參數(shù)峦树;下面的幾種 無返回值表達(dá)的意思是一樣的
func sayHello()->Void{ print("Hello") } func sayHello()->(){ print("Hello") } func sayHello(){ print("Hello") }
-
-
2.2、函數(shù)的隱式返回
func sum(num1:Int,num2:Int) -> Int{ num1 + num2 } sum(num1: 1, num2: 3)
提示:如果整個(gè)函數(shù)的整體是一個(gè)單一的表達(dá)式簿废,那么函數(shù)會(huì)隱式返回這個(gè)表達(dá)式
-
2.3空入、返回元組 和 實(shí)現(xiàn)多值返回
func calculate(v1: Int,v2:Int) -> (sum:Int,difference:Int,average:Int){ let sum = v1 + v2 return (sum,v1-v2,sum>>1) } let result = calculate(v1: 20, v2: 10) result.sum // 和 result.difference // 差 : 10 result.average // 二進(jìn)制右移 是 平均值 15
提示:
sum>>1
代表值的二進(jìn)制數(shù) 右移 求平均值 -
2.4、函數(shù)的文檔注釋 參考蘋果的鏈接
/// 求和 【概述】 /// /// 將兩個(gè)整數(shù)相加 【更詳細(xì)的描述】 /// /// - Parameter v1: 第 1 個(gè)整數(shù) /// - Parameter v2: 第 2 個(gè)整數(shù) /// - Returns : 兩個(gè)整數(shù)的和 /// /// - Note:傳入兩個(gè)整數(shù)即可 【批注】族檬,有沒有什么特殊的用法 /// func sum(_ v1:Int,_ v2:Int) -> Int{ return v1 + v2 } sum(10, 20)
效果如下:
-
2.5歪赢、參數(shù)標(biāo)簽
-
可以修改參數(shù)標(biāo)簽
func goToWork(at time:String){ print("this time is \(time)") } goToWork(at: "08:00")
打印結(jié)果:
this time is 08:00
-
可以使用下劃線
_
省略參數(shù)標(biāo)簽func sum(_ v1:Int,_ v2:Int) -> Int{ return v1 + v2 } sum(10, 20)
-
-
2.6、默認(rèn)參數(shù)值(Default Parameter Value)
-
參數(shù)可以有默認(rèn)值
func check(name:String = "noPerson",age:Int,job:String = "iOS"){ print("名字=\(name) 年齡=\(age) 工作=\(job)") } check(name: "老大", age: 19, job: "iOS") check(name: "老二", age: 19) check(age: 10, job: "前端") check(age: 16)
提示:只要是有默認(rèn)參數(shù)值的就可以不傳參數(shù)
C++的默認(rèn)參數(shù)值有個(gè)限制:必須從右往左設(shè)置单料。由于Swift擁有參數(shù)標(biāo)簽埋凯,因此并沒有此類限制
-
但是在省略參數(shù)標(biāo)簽時(shí),需要特別注意扫尖,避免出錯(cuò)白对,如下
這里的two
不可以省略參數(shù)標(biāo)簽func test(_ one:Int = 10,two:Int,_ three:Int = 20){ print("打印 one = \(one) two = \(two) three = \(three)") } test(two: 9)
-
-
2.7、可變參數(shù)(Variadic Parameter)
func sum(_ numbers: Int...) -> Int { var total = 0 for i in numbers { total += I } return total } sum(1,2,3)
提示:一個(gè)函數(shù) 最多只能有1個(gè) 可變參數(shù)换怖;緊跟在可變參數(shù)后面的參數(shù)不能省略參數(shù)標(biāo)簽甩恼,如下
func test(_ numbers:Int...,string:String,_ other:String){ } test(1,2,3, string: "iOS", "Rose")
參數(shù) string 標(biāo)簽不能省略
-
2.8、Swift 自帶的 print 函數(shù)
/// - Parameters: /// - items: Zero or more items to print. /// - separator: A string to print between each item. The default is a single /// space (`" "`). /// - terminator: The string to print after all items have been printed. The /// default is a newline (`"\n"`). public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
分析: 第一個(gè)參數(shù)是要打印的值沉颂,第二個(gè)參數(shù)是:連接第一個(gè)參數(shù)值的 字符条摸,第三個(gè)參數(shù)是:默認(rèn)是
\n
換行,如下例子print(1,2,3,4,5) print(1,2,3, separator: "_") print(1,2,3, separator: "_", terminator: "") print("哈哈")
打印結(jié)果是: 最后一行不換行铸屉,因?yàn)?
"\n"
被用""
取代了1 2 3 4 5 1_2_3 1_2_3哈哈
-
2.9钉蒲、輸入輸出參數(shù) (In-Out Parameter)
可以用 inout 定義一個(gè)輸入輸出函數(shù):可以在函數(shù)內(nèi)部修改外部實(shí)參的值func swapValues(_ v1:inout Int,_ v2:inout Int){ let tem = v1 v1 = v2 v2 = tem } var num1 = 10 var num2 = 20 swapValues(&num1, &num2) print(num1,num2)
打印結(jié)果:20 10
-
提示:下面的函數(shù)與上面的等價(jià):傳進(jìn)去的是 內(nèi)存地址,所以可以修改其外部參數(shù)的值
func swapValues(_ v1:inout Int,_ v2:inout Int){ (v1,v2) = (v2,v1) }
可變參數(shù)不能標(biāo)記為 inout
輸入輸出參數(shù)不能有默認(rèn)值
輸入輸出參數(shù)不能傳入常量(let)彻坛、字面量作為實(shí)參顷啼。也就是可以被多次賦值的 變量
inout
參數(shù)的本質(zhì)是地址傳遞 (引用傳遞)
-
-
2.10踏枣、函數(shù)重載 (Function Overload)
規(guī)則:函數(shù)名相同
參數(shù)個(gè)數(shù)不同 || 參數(shù)類型不同 || 參數(shù)標(biāo)簽不同func sum(v1:Int,v2:Int) -> Int{ return v1 + v2 }
參數(shù)個(gè)數(shù)不同
func sum(v1:Int,v2:Int,v3:Int) -> Int{ return v1 + v2 }
參數(shù)類型不同
func sum(v1:Int,v2:Double) -> Double{ return Double(v1) + v2 } func sum(v1:Double,v2:Int) -> Double{ return v1 + Double(v2) }
參數(shù)標(biāo)簽不同
func sum(_ v1:Int,_ v2:Int) -> Int{ return v1 + v2 } func sum(a:Int,b:Int) -> Int{ return a + b }
-
函數(shù)重載注意點(diǎn)一: 返回值類型 與 函數(shù)重載 無關(guān)
func sum(a:Int,b:Int) -> Int{ return a + b } func sum(a:Int,b:Int) { } sum(a: 1, b: 2)
-
函數(shù)重載注意點(diǎn)二:默認(rèn)參數(shù)值和函數(shù)重載一起使用產(chǎn)生二義性時(shí),編譯器并不會(huì)報(bào)錯(cuò)(在 C++ 中會(huì)報(bào)錯(cuò))
func sum(a:Int,b:Int) -> Int { return a + b } func sum(a:Int,b:Int,c:Int = 10) -> Int { return a + b + c } // 會(huì)調(diào)用 sum(a:Int,b:Int)钙蒙,這樣不太好茵瀑,還不如編譯器報(bào)錯(cuò) sum(a: 1, b: 2)
-
函數(shù)重載注意點(diǎn)三:可變參數(shù),省略參數(shù)標(biāo)簽仪搔,函數(shù)重載一起使用產(chǎn)生二義性時(shí)瘾婿,編譯器有可能會(huì)報(bào)錯(cuò)
func sum(a:Int,b:Int) -> Int{ return a + b } func sum(_ a:Int,_ b:Int) -> Int { return a + b } func sum(_ numbers: Int...)->Int{ var total = 0 for i in numbers { total += I } return total }
調(diào)用
sum(1,2)
報(bào)錯(cuò):Ambiguous use of 'sum'
-
-
2.11蜻牢、內(nèi)聯(lián)函數(shù)(Inline function)
- 如果開啟了編譯器優(yōu)化(Release模式默認(rèn)會(huì)開啟優(yōu)化)烤咧,編譯器會(huì)默認(rèn)將某些函數(shù)變成內(nèi)聯(lián)函數(shù)
- 將函數(shù)調(diào)用展開成函數(shù)體
將函數(shù)調(diào)用展開成函數(shù)體
不會(huì)被內(nèi)聯(lián)的函數(shù):函數(shù)體比較長(zhǎng)、包含遞歸調(diào)用抢呆、包含動(dòng)態(tài)派發(fā)......
- 如果開啟了編譯器優(yōu)化(Release模式默認(rèn)會(huì)開啟優(yōu)化)烤咧,編譯器會(huì)默認(rèn)將某些函數(shù)變成內(nèi)聯(lián)函數(shù)
-
2.12煮嫌、函數(shù)類型:每一個(gè)函數(shù)都是有類型的,函數(shù)的類型由:形式參數(shù)類型抱虐,返回值類型組成
-
例子一:無參數(shù)無返回值
func test() {}
-
例子二:有參數(shù)有返回值
func test(a: Int, b: Int) -> Int { return a + b }
-
例子三:調(diào)用的時(shí)候不需要傳參數(shù)標(biāo)簽
func test(_ a: Int ,_ b: Int) -> Int { return a + b } test(2,3) // 調(diào)用的時(shí)候不需要傳參數(shù)標(biāo)簽
-
-
2.13昌阿、函數(shù)類型作為函數(shù)參數(shù)
func sum(a: Int, b: Int) -> Int{ return a + b } func difference(a:Int,b:Int) -> Int{ return a - b } func printResult(_ mathFn:(Int,Int)->Int,_ a:Int,_ b:Int){ print("result=\(mathFn(a,b))") } printResult(sum, 5, 2) // 打印結(jié)果:result=7 printResult(difference, 5, 2) // 打印結(jié)果:result=3
-
2.14、函數(shù)類型作為函數(shù)返回值
func next(_ input: Int) -> Int { return input + 1 } func previous(_ input: Int) -> Int { return input - 1 } func forward(_ forward: Bool) -> (Int) -> Int { return forward ? next:previous } print(forward(true)(3)) // 結(jié)果:4 print(forward(false)(3)) // 結(jié)果:2
提示:返回值是函數(shù)的函數(shù)恳邀,叫做高階函數(shù)(Height-Order Function)
-
2.15懦冰、typealias: 用來給類型起別名
typealias Byte = Int8 typealias Short = Int16 typealias Long = Int64
如下:
typealias Date = (year:Int,month:Int,day:Int) func test(_ date:Date){ print(date.year,date.month,date.day) } test((2019,6,19))
例二:(重點(diǎn)????????????????????)
typealias IntFn = (Int,Int) -> Int func difference(v1:Int,v2:Int)->Int{ return v1-v2 } let fn:IntFn = difference fn(20,10) func setFn(_ fn:IntFn){} setFn(difference) func getFn() -> IntFn{ return difference }
-
2.16、嵌套函數(shù): 將函數(shù)定義在函數(shù)的內(nèi)部
func forward(_ forward:Bool)->(Int)->Int{ func next(_ input:Int)->Int{ return input + 1 } func previous(_ input:Int)->Int{ return input - 1 } return forward ? next:previous } forward(true)(3) // 4 forward(false)(3) // 2
三谣沸、枚舉
-
3.1刷钢、枚舉的基本用法
enum Direction { case north case south case east case west } enum Direction { case north,south,east,west }
使用如下:
var dircetion = Direction.north dircetion = Direction.west dircetion = .south print("dircetion=\(dircetion)") let dir = Direction.north switch dir { case .north: print("north") case .south: print("south") case .east: print("east") case .west: print("west") }
-
3.2、關(guān)聯(lián)值(Associated Values)
-
有時(shí)會(huì)將枚舉的成員值跟其他類型的關(guān)聯(lián)存儲(chǔ)在一起乳附,會(huì)非常有用
enum Score { case points(Int) case grade(Character) } var source = Score.points(96) source = .grade("A") switch source { case let .points(i): print(i,"points") case let .grade(i): print("grade",i) }
打印結(jié)果:grade A
-
下面代碼必要的時(shí)候 let 可以改為 var
enum Date{ case digit(year:Int,month:Int,day:Int) case string(String) } var date = Date.digit(year:2019,month:6,day:21) date = .string("2019-6-21") switch date { case .digit(let year,let month,let day): print(year,month,day) case let .string(value): print(value) }
打印結(jié)果:2019-6-21
-
關(guān)聯(lián)值舉例
關(guān)聯(lián)值手勢(shì)舉例enum Password{ case number(Int,Int,Int,Int) case gesture(String) } var pwd = Password.number(3,5,7,8) pwd = .gesture("12368") switch pwd { case let .number(n1,n2,n3,n4): print("number is \(n1) \(n2) \(n3) \(n4)") case let .gesture(str): print("gesture is",str) }
打印結(jié)果:gesture is 12368
-
-
3.3内地、原始值 (Raw Values):枚舉成員可以使用相同類型的默認(rèn)值預(yù)先關(guān)聯(lián),這個(gè)默認(rèn)值叫做:原始值
enum PlayingCards : Character{ case A = "a" case B = "b" case C = "c" case D = "d" } var suit = PlayingCards.A print(suit) // A print(suit.rawValue) // a print(PlayingCards.D.rawValue) // d enum Grade : String{ case perfect = "A" case great = "B" case good = "C" case bad = "D" } print(Grade.perfect.rawValue) // A print(Grade.great.rawValue) // B print(Grade.good.rawValue) // C print(Grade.bad.rawValue) // D
-
3.4赋除、隱式原始值 (Implicitly Assignd RawValues):如果枚舉的原始類型是Int阱缓、String 類型,Swift會(huì)自動(dòng)分配原始值
-
String 類型
enum Direction { case north,south,east,west } print(Direction. north) // north print(Direction. north.rawValue) // north
等價(jià)于
enum Direction: String { case north = "north" case south = "south" case east = "east" case west = "west" }
-
Int 類型
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
-
Int 類型設(shè)置了默認(rèn)原始值举农,設(shè)置了值之后 Swift會(huì)自動(dòng)遞增原始值
enum Season:Int{ case spring = 2,summer,autumn = 5,winter } print(Season.spring.rawValue) // 2 print(Season.summer.rawValue) // 3 print(Season.autumn.rawValue) // 5 print(Season.winter.rawValue) // 6
-
-
3.5荆针、遞歸枚舉 (Recursive Enumeration)
enmu 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 two = ArithExpr.number(2) let sum = ArithExpr.sum(five,four) let difference = ArithExpr.difference(sum,two) 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)
提示:使用
indirect
關(guān)鍵字修飾的枚舉值表示這個(gè)枚舉是可以遞歸的,即此枚舉值中的相關(guān)值使用其枚舉類型本身颁糟。 -
3.7航背、MemoryLayout: 獲取數(shù)據(jù)類型占用的內(nèi)存大小
enum Password{ case number(Int,Int,Int,Int) // 32 字節(jié) case other // 1 個(gè)字節(jié)就搞定了 } MemoryLayout<Password>.stride // 40 分配占用的內(nèi)存空間大小 MemoryLayout<Password>.size // 33 實(shí)際用到的空間大小 MemoryLayout<Password>.alignment // 8 對(duì)齊參數(shù) var password = Password.number(2,3,4,5) password = .other MemoryLayout.stride(ofValue:password) // 40 MemoryLayout.size(ofValue:password) // 33 MemoryLayout.alignment(ofValue:password) // 8
- 提示1:stride:范圍 與 size 的區(qū)別
size: 分配占用的內(nèi)存空間大小
stride: 實(shí)際用到的空間大小
內(nèi)存對(duì)齊是 8個(gè)字節(jié) ,所以上面是 32+1 = 40 - 提示2:N個(gè)字節(jié)存儲(chǔ)關(guān)聯(lián)值(N取內(nèi)存最大的關(guān)聯(lián)值)滚停,比如number(Int,Int,Int,Int)就是 4*8=32 里面的元素是關(guān)聯(lián)值沃粗,1個(gè)字節(jié)用來存儲(chǔ)成員值(也就是case的個(gè)數(shù))
- 提示1:stride:范圍 與 size 的區(qū)別
-
3.8、枚舉拓展
-
拓展一:枚舉的關(guān)聯(lián)值(3.2) 和 默認(rèn)原始值(3.3) 的區(qū)別
分析:枚舉的關(guān)聯(lián)值 的值 是寫到枚舉的內(nèi)存中的键畴,而枚舉的原始值是固定死的最盅,沒有寫入枚舉的內(nèi)存中
舉例如下:enum Season:Int{ case spring,summer,autumn,winter } MemoryLayout<Season>.stride // 1 MemoryLayout<Season>.size // 1 MemoryLayout<Season>.alignment // 1
-
拓展二:思考下面枚舉變量的內(nèi)存布局
enum TestNum { case test1,test2,test3,test4 } print( MemoryLayout<TestNum>.stride) // 1 分配占用的內(nèi)存空間大小 print(MemoryLayout<TestNum>.size) // 1 實(shí)際用到的空間大小 print(MemoryLayout<TestNum>.alignment) // 1 對(duì)齊參數(shù)
-
提示1:直接查看內(nèi)存布局
內(nèi)存布局的展示 -
提示2:我們還可以在Debug -> Debug Workflow -> View Memory 手動(dòng)輸入內(nèi)存地址來查看內(nèi)存布局突雪,如下
在Debug -> Debug Workflow -> View Memory 手動(dòng)輸入內(nèi)存地址來查看內(nèi)存布局
var t = TestNum.test1 print( Mems.ptr(ofVal: &t)) t = .test2 t = .test3 t = .test4
- 上面是采取一個(gè)字節(jié)來存儲(chǔ)枚舉變量的數(shù)據(jù),一個(gè)字節(jié) 00 用來存儲(chǔ) test1涡贱、一個(gè)字節(jié) 01 用來存儲(chǔ) test2咏删、一個(gè)字節(jié) 02 用來存儲(chǔ) test3、一個(gè)字節(jié) 03 用來存儲(chǔ) test4问词,其實(shí)也就是來區(qū)分成員變量的值
- 按照上面的一個(gè)字節(jié)來區(qū)分的話督函,一個(gè)字節(jié)的范圍是 0x00~0xFF,最大也就是 256,每一個(gè)case也就是一個(gè)成員變量激挪,其實(shí)
case test1,test2,test3,test4
相當(dāng)于四個(gè)case
-
-
拓展三:看下面一個(gè)復(fù)雜的枚舉
enum TestNum { case test1(Int,Int,Int) case test2(Int,Int) case test3(Int) case test4(Bool) case test5 } print(MemoryLayout<TestNum>.size) // 25分配的內(nèi)存大小 print(MemoryLayout<TestNum>.stride) // 32 實(shí)際使用的內(nèi)存大小 print(MemoryLayout<TestNum>.alignment) // 8 內(nèi)存對(duì)齊的字節(jié)數(shù)
分析:分配內(nèi)存的規(guī)則:如果有多個(gè)case,那么必有一個(gè)字節(jié)存儲(chǔ)來區(qū)分成員變量case辰狡,再看case關(guān)聯(lián)值最多的那個(gè)有多少個(gè)關(guān)聯(lián)值,N個(gè)字節(jié)存儲(chǔ)關(guān)聯(lián)值(N取內(nèi)存最大的關(guān)聯(lián)值)垄分,比如number(Int,Int,Int)就是 3*8=24 里面的元素是關(guān)聯(lián)值宛篇,1個(gè)字節(jié)用來存儲(chǔ)成員值(也就是case的個(gè)數(shù)),那么分配的內(nèi)存是 32薄湿,實(shí)際用了 24+1= 25
-
拓展四:看下面一個(gè)有意的的枚舉
enum TestNum { case test1(Int,Int,Bool,Bool) case test2(Int,Int) } print(MemoryLayout<TestNum>.size) // 實(shí)際使用的內(nèi)存大小 18 print(MemoryLayout<TestNum>.stride) // 分配的內(nèi)存大小 24 print(MemoryLayout<TestNum>.alignment) // 內(nèi)存對(duì)齊的字節(jié)數(shù) 8
分析:8+8+最后一個(gè)Bool【1(第一個(gè)bool)+1(區(qū)別成員變量) 】= 17 這是編譯器的進(jìn)一步優(yōu)化叫倍,它把case標(biāo)志放在最后一個(gè)Bool字節(jié)里去了,相當(dāng)于最后一個(gè)Bool豺瘤,它的那個(gè)字節(jié)里面包含了case成員信息吆倦。因?yàn)锽ool類型只有2種取值,true是1坐求,false是0蚕泽,只會(huì)用到1個(gè)二進(jìn)制位,所以Bool的1個(gè)字節(jié)還是有多余空間的瞻赶,如果是Int這些赛糟,就有可能要把它自己的8個(gè)字節(jié)都用完。所以無法拿來放case砸逊,比如0xFF FF FF FF FF FF FF FF璧南,占滿了8字節(jié),而Bool师逸,你的8個(gè)二進(jìn)制位司倚,不管存儲(chǔ)true還是false,都只需要用到1位
-
拓展五:沒有關(guān)聯(lián)值
enum TestNum { case test1,test2,test3,test4 } print(MemoryLayout<TestNum>.size) // 實(shí)際使用的內(nèi)存大小 1 print(MemoryLayout<TestNum>.stride) // 分配的內(nèi)存大小 1 print(MemoryLayout<TestNum>.alignment) // 內(nèi)存對(duì)齊的字節(jié)數(shù) 1
分析:這里的 1 僅僅是為了區(qū)分 成員變量使用的
-