枚举可选型

开始今天的内容之前先补充个关键字:
@inline

@inline

内敛函数
如果开启了编译器优化(Release模式默认开启优化),编译器会自动将某些函数变成内敛函数.

image.png

哪些函数不会被自动内敛?
1、代码比较长
2、递归调用函数
3、动态派发函数

可以通过never、__always关键字控制一下内敛:

  • 永远不会被内敛(即便开启了编译器优化)
@inline(never) func test() {
     print("test")
 }
  • 开启编译器优化后,即使代码比较长,也会被内敛(递归调用函数、动态派发函数除外)
@inline(__always) func test() {
      print("test")
 }

release环境下,编译器已经开启优化,会自动决定哪些函数需要内敛,因此没必要使用@inline

枚举的基本用法

回顾一下以前的C语言下枚举的本质就是整型(0、1、2 ...)

enum Score {
    case one
    case two
    case three
} 
let e = Score.one 

关联值(Associated Values)

案例1

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

switch score {
case let .point(i):
    print(i,"points")
case let .grade(i):
    print("grade", i)
}

案例2

enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}

var date = Date.digit(year: 2020, month: 6, day: 26)
date = Date.string("2020-06-26")

switch date {
case .digit(year: let year, month: let month, day: let day):
    print(year, month, day)
case let .string(value)
    print(value)
} 

结论,关联值将各种类型的数据直接存储在枚举变量里面去。

原始值(Raw Values)

enum PokerSuit: Character {
    case spade = "♠️"
    case heart = "♥️"
    case diamond = "♦️"
    case club = "♣️"
}

枚举成员可以使用相同类型的默认值进行关联,这个默认值叫做:原始值
注:这里的冒号不是继承哈!

使用如下:

var suit = PokerSuit.spade
print(suit)// spade
print(suit.rawValue)//♠️
print(PokerSuit.club.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
}
print(Direction.north) // north
print(Direction.north.rawValue) // north

再比如:

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)

实例1: 算术表达式

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 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 difference(left) - difference(right)
    }
}
calculate(difference)

MemoryLayout

可以使用MemoryLayout获取数据类型占用的内存大小(在OC里面有个sizeOf)

MemoryLayout是支持泛型的.
实例1

enum Password {
   case number(Int, Int, Int, Int)
   case other
}
var pwd = Password.number(1,2,3,4) // 占用多少个字节?
pwd = .other // 这个呢?

MemoryLayout<Password>.stride//40, 分配占用的空间大小 
MemoryLayout<Password>.size//33, 实际用到的空间大小
MemoryLayout<Password>.alignment//8
enum Season {
  case spring, summer, autumn, winter
}
MemoryLayout<Season>.stride//1
MemoryLayout<Season>.size//1
MemoryLayout<Season>.alignment//1
var age = 10
MemoryLayout.stride(ofValue: age)// 8
MemoryLayout.size(ofValue: age) // 8
MemoryLayout.alignment(ofValue: age)// 8

这种类型的枚举

enum Season {
    case spring, summer, autumn, winter
}
var s = Season.spring// 1个字节

//原始值是Int
enum Season : Int {
    case spring = 1, summer = 2, autumn = 3, winter = 4
}
var s = Season.spring// 1个字节
var s1 = Season.summer// 1个字节
var s2 = Season.autumn// 1个字节

//原始值是String
enum Season : String {
    //序号 0 1 2 3
    case spring = "1", summer = "2", autumn = "3", winter = "3"
}
var s = Season.spring// 1个字节
var s1 = Season.summer// 1个字节
var s2 = Season.autumn// 1个字节

思考1:Apple对原始值是怎么实现的?
思考2:上面例子Password实际用到的空间大小为什么实33?32不行吗?

关联值、原始值的区别?
关联值把传进来的关联的值存放到枚举变量里面
关联值的特点:可以传入不同的值,意味着每一个枚举变量都要有内存来存储不同的值.

var pwd1 = Password.number(10,999,10000,10000)

原始值和枚举成员绑定在一起,固定不变的一个值.所以没有必要给它分配内存空间放到枚举变量里面

var s = Season.spring// 1个字节
var s1 = Season.summer// 1个字节
var s2 = Season.autumn// 1个字节

猜测:默认值可以这样实现

enum Season3: Int {
    case spring = 1, summer, autumn, winter
    func rawValue() {
        if self == .spring return 1
       else if self == .summer return 2
       else if self == .autumn return 3
        ...
    }
}

可选项(Optional)

类型后面加?

var name: String? = "Jack"
name = nil
var age: Int? // 默认值为nil
age = 10
age = nil

等价

var age: Int?
var age: Int? = nil

不等价

var age: Int
var age: Int = 0

强制解包(Forced Unwarpping)

可选项是对其他类型的包装

var age1: Int?// 把Int类型的数据添加到盒子里面
age1 = 10
var age2 = age1! + 2

对nil强制解包会carsh

实例

var num = "abc123"
var numInt = Int(num)
if numInt != nil {
    print("字符串转换成功\(numInt!)")
} else {
    print("字符串转换失败")
}

可选项绑定(Optional Binding)

实例1

if let number = Int("123") {
    print("字符串转换成功\(numInt!)")
} else {
    print("字符串转换失败")
}

实例2

if let season = Season(rawValue: 6) {
} else {}

while循环中使用可选项绑定

let strs = ["10","10","abc","5","10","10",]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
    sum+=num
    index+=1
}

空合并运算符??

源码如下

func ?? <T>(optional: T?, defaultValue: @autoclosure() throws -> T?) throws -> T?
func ?? <T>(optional: T?, defaultValue: @autoclosure() throws -> T) throws -> T

a??b
1、a是可选项
2、b是可选项或者不是可选项
3、b跟a存储类型必须相同
4、如果a不为nil,返回a;如果a为nil,返回b
5、如果b不是可选项,返回a时会自动解包,
实例1

let a: Int? = 1
let b: Int? = 2
let c = a ?? b // c是Int?, Optional(1)

实例2

let a: Int? = nil
let b: Int? = 2
let c = a ?? b // c是Int?, Optional(2)

实例3

let a: Int? = nil
let b: Int? = nil
let c = a ?? b // c是Int?, nil

实例4

var a: Int? = nil
var b:Int = 1
let c = a ?? b // c是Int,  1

使用场景:

var a: Int? = nil
var b:Int = 2
// 如果不使用??运算符
let c: Int
if let temp = a {
  c = temp
} else {
  c = b
}

多个??一起使用

实例1

var a: Int? = 1
var b: Int? = 2
let c = a ?? b ?? 3 // c是Int, 1

实例2

var a: Int? = nil
var b: Int? = 2
let c = a ?? b ?? 3 // c是Int, 2

实例3

var a: Int? = nil
var b: Int? = nil
let c = a ?? b ?? 3 // c是Int, 3

注意⚠️

var a: Int? = nil
var b: Int? = nil
let c = a ?? 2 ?? b // 这样写是会警告的,想想为什么?

??跟if let配合使用

var a: Int? = nil
var b: Int? = 2
if let c = a ?? b {
   print(c)
}

类似于: if a!=nil || b != nil
使用场景: 如果有多个可选项,其中有一个可选项不为nil,就可以进来.

var a: Int? = nil
var b: Int? = 2
if let c = a, let d = b {
   print(c)
}

类似于: if a!=nil && b != nil

if 语句实现登录

看例子前先补充一下知识点:

  • swift里面通过key从字典里面取出来的value是可选类型的.
  • 数组里面通过下标取出来的是真实类型(所以数组是否越界需要我们自己判断呀)
func login(_ info: [String: String]) {
    let userName: String
    if let temp = info["username"] {
        userName = temp
    } else {
        print("请输入用户名")
        return
    }
    let pwd: String
    if let temp = info["password"] {
        pwd = temp
    } else {
        print("请输入密码")
        return
    }

    print("用户名:\(userName),密码:\(pwd)登录中")
}

guard语句

当guard语句的条件为false时,就会执行大括号里面的代码;
当guard语句的条件为true时,就会跳出guard语句;
guard语句特别适合用来提前退出.

guard 条件 else {
// do something
退出当前作用域
// return、break 、continue 、throw error
}

当guard语句进行可选绑定时,绑定的常量(let)、变量(var)也能在外层作用域使用

func login1(_ info: [String: String]) {
   guard let userName = info["username"] else {
       print("请输入用户名")
       return
   }
   guard let pwd = info["password"] else {
       print("请输入密码")
       return
   }

   print("用户名:\(userName),密码:\(pwd)登录中")
}

隐私解包(Implicitly Unwrapped Optional)

在某些情况下,可选值一旦被设定值之后,就会一直拥有值,
在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值.
可以在类型后面加一个感叹号!定义一个隐式解包的可选项
以前写法

let num1: Int? = 10
let num2: Int = num1!

隐私解包的可选项

var num1: Int! = 10
var num2: Int = num1

无论是Int!还是Int?都是可选类型

var num1: Int! = 10
var num2: Int = num1
if num1 != nil {
  print(num1 + 6)
}
if let num3 = num1 {
  print(num3)
}
let num1: Int! = nil
//Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
let num2: Int = num1

感觉这种类型没啥用?有木有

使用场景:如果能确保一定有值可以使用
希望别人给你值,不希望别人给你nil,但是别人可能给你的是nil.

多重可选项

var num1: Int?
var num2: Int?? = num1
var num3: Int?? = 10

frame variable -R

简写:fr v -R 变量名 查看变量结构

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容