Swift: 06-可选项

//可选项 Optional
//可选项,一般也叫可选类型,它允许将值设置为nil,默认不允许设置为nil
//在类型名称后面加个问号? 来定义一个可选项
var str: String? = "123"
str = nil

var age: Int? //可选值默认就是nil,var age:Int等价于var age:Int?= nil
age = 10
age = nil

var array = [1, 15, 40, 29]
func get(_ index:Int) -> Int? { //如果函数返回值可能为nil,返回值类型就要加?
    if index < 0 || index >= array.count {
        return nil
    } else {
        return array[index]
    }
}
print(get(1)) //Optional(15)
print(get(-1))//nil
print(get(4)) //nil

var age1:Int = 15
print(age1) //15

var age2:Int? = 15
print(age2) //Optional(15), 这个是可选类型


//强制解包(Forced Unwrapping)
//可选项是对其他类型的一层包装,可以将它理解为一个盒子
//如果为nil,那么它是个空盒子
//如果不为nil,那么盒子里面装的是:被包装类型的数据
var age3: Int? //默认就是nil
age3 = 10
age3 = nil

把10赋值给age3,如图相当于Int10这个蓝色的框框去掉
//如果要从可选项中取出被包装的数据(将盒子里面装的东西取出来),需要使用!进行强制解包
var age4: Int? = 10
var ageInt: Int = age4!
ageInt += 10

var age5: Int? = 10
var num = age5! + 20 //30
print(age5) //Optional(10)
//强制解包只是取值的过程,并不会修改age5本身的可选项值

//如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误,crash奔溃
//报错信息:Fatal error: Unexpectedly found nil while unwrapping an Optional value
var age6: Int?
//age6!  //这里会crash,报错先注释掉了

//判断可选项是否包含值
let number = Int("123")
if number != nil {
    print("字符串转换成功:\(number!)") //这里的number可能转换失败,返回0或者-1都不合适,只能返回nil,所以这种通过字符串转换成Int类型,number是可选类型,需要强制解包
} else {
    print("字符串转换整数失败,返回nil")
}
// 字符串转换整数成功:123
//var num = Int("xxx")


//可选项绑定
//可以使用可选项绑定来判断可选项是否包含值
//如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false
if let number = Int("123") {
    print("字符串转换整数成功:\(number)")
    // number是强制解包之后的Int值
    // number作用域仅限于这个大括号
} else {
    print("字符串转换整数失败")
}

enum Season: Int {
    case spring = 1, summber, autumn, winter
}
//var sea = Season(rawValue: 2) 这里的sea是可选项,因为获取枚举传入原始值,可能不存在对应原始值的枚举变量
if let season = Season(rawValue: 6) { //这里需要注意,可选项绑定会帮忙自动解包,获取到的直接就是Season类型,而不是Season?可选类型
    switch season {
    case  .spring:
        print("the season is spring")
    default:
        print("the season is other")
    }
} else {
    print("no such season")
}
//no such season


// 等价写法
if let first = Int("4") {
    if let second = Int("42") {
        if first < second && second < 100 {
            print("\(first) < \(second) < 100")
        }
    }
}
//上面的写法等价于下面的写法:
if let first = Int("4"), //这里的可选项绑定,必须要使用, 逗号隔开,不能直接使用&&符合拼接
    let second = Int("42"),
    first < second && second < 100 {
    
}

//while循环中使用可选项绑定
//遍历数组,将遇到的正数都加起来,如果遇到负数或者非数字,停止遍历
var strs = ["10", "20", "30", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
    sum += num
    index += 1
}
print(sum) //60 10+20+30=60


//空合并运算符 ?? (Nil-Coalescing Operator)
//public func ?? <T>(optional:T?, defaultValue:@autoclosure () throws -> T?) rethrows -> T?
//
//public func ?? <T>(optional:T?, defaultValue:@autoclosure () throws -> T) rethrows -> T
//a ?? b
//1. 其中a必须是可选项,
//2. b是可选项 或者不是可选项
//3. b和a的存储类型必须相同
//4. 如果a 不为nil,就返回a
//5. 如果a 为nil,就返回b
//6. 如果b 不是可选项,返回a时自动解包

let a: Int? = 1
let b: Int? = 2
let c = a ?? b //1

let a1: Int? = nil
let b1: Int? = 2
let c1 = a1 ?? b1 //2

let a2: Int? = nil
let b2: Int? = nil
let c2 = a2 ?? b2 //nil

let a3: Int? = 1
let b3: Int = 2
let c3 = a3 ?? b3 //c是Int, 1

let a4: Int? = nil
let b4: Int = 2
let c4 = a4 ?? b4 //2

let a5: Int? = nil
let b5: Int = 2
//如果不使用?? 运算符
let c5: Int
if let tmp = a5 { //可选项会绑定失败
    c5 = tmp
} else {
    c5 = b5
}

//多个??一起使用
let a6: Int? = 1
let b6: Int? = 2
let c6 = a6 ?? b6 ?? 3 //c是Int,1

let a7: Int? = nil
let b7: Int? = 2
let c7 = a7 ?? b7 ?? 3 //c7是Int,2

let a8: Int? = nil
let b8: Int? = nil
let c8 = a8 ?? b8 ?? 3 //c8是Int,3

let a9: Int? = 1
let b9: Int? = 2
let c9 = a9 ?? 3 ?? b //c9是Int,这种会警告,??左边只能是可选项,不可以是Int

//??跟if let配合使用
let a10: Int? = nil
let b10: Int? = 2
if let c10 = a10 ?? b10{
    print(c10)
}
//类似于 if a != nil || b != nil, 并且可以自动解包赋值给 c10
//类似这种会有多个可选项解包可以使用多个 ?? 连接起来使用

if let c = a, let d = b {
    print(c)
    print(d)
}
//类似于 if a != nil && b != nil
//a自动解包成功并且b自动解包成功,if条件才满足,且的关系

//if 语句实现登陆
func login(_ info: [String: String]) {
    var username: String
    if let tmp = info["username"] {
        username = tmp
    } else {
        username = ""
        print("请输入用户名")
        return
    }
    
    var password: String
    if let tmp = info["password"] {
        password = tmp
    } else {
        password = ""
        print("请输入密码")
        return
    }
    //if username...
    //if password...
    print("用户名:\(username)","密码:\(password)","登陆ing")
}

login(["username":"jack","password":"123456"]) //用户名:jack 密码:123456 登陆ing
login(["password":"123456"]) //请输入用户名
login(["username":"jack"]) //请输入密码

//guard 语句
//当guard 语句的条件为false时,就会执行大括号里面的代码
//当guard 语句为true时,就会跳过guard语句
//guard语句特别适合用来 “提前退出”
func test() {
    guard 1>2 else {
        //do something
        //一定要推出当前作用域
        return
    }
}
//当使用guard语句进行可选项绑定时,绑定的常量(let),变量(var)也能在外层作用域中使用

//前面登陆的代码可以用guard来优化下:

func login2(_ info:[String:String]) {
    guard let username = info["username"] else {
        print("请输入用户名")
        return
    }
    guard let password = info["password"] else {
        print("请输入密码")
        return
    }
    //if username...
    //if password...
    print("用户名:\(username)","密码:\(password)","登陆ing")
}
login2(["username":"jack","password":"123456"]) //用户名:jack 密码:123456 登陆ing
login2(["password":"123456"]) //请输入用户名
login2(["username":"jack"]) //请输入密码


//隐式解包 (Implicitly Unwrapped Optional)
//某些情况下,可选项一旦被设定值之后,就会一直拥有值
//这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
//可以在类型后门加个感叹号!定义一个隐式解包的可选项
let num1: Int! = 10
let num2: Int = num1
if num1 != nil {
    print(num1 + 6)
}

if let num3 = num1 {
    print(num3)
}

//尽量不要使用!,大多数情况下使用?的可选项
let num4:Int! = nil //加了!也是可选项,只不过是隐式解包的可选项
//let num5:Int = num4
//这里会报错:Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
//有一种情况,就是如果你希望别人传给你的参数必须是Int值,不能为nil,如果为nil,就崩溃掉,那就可以使用这种类型,这种也是不合理,还是期望使用?。


//字符串插值
//可选项在字符串插值或者直接打印时,编译器会发出警告
var age7: Int? = 10
print("my age is \(age7)") //my age is Optional(10)
//有三种方法可以消除警告
//1
print("my age is \(age7!)") //my age is 10
//2
print("my age is \(String(describing: age7))") //my age is Optional(10)
//3
print("my age is \(age7 ?? 0)") // my age is 10

//多重可选项
var num11: Int? = 10
var num22: Int?? = num11
var num33: Int?? = 10
print(num22 == num33) //true
image.png
var num111: Int? = nil
var num222: Int?? = num11
var num333: Int?? = nil

print(num222 == num333) //false
print(num111 == num333) //false

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

推荐阅读更多精彩内容