1. 枚举
1.1 枚举的基本用法
enum Direction{
case north
case south
case east
case west
}
var dir = Direction.west
dir = Direction.east
dir = .north
print(dir)//north
switch dir {
case .north:
print("north")
case .south:
print("south")
case .east:
print("east")
case .west:
print("west")
}
1.2 关联值
- 有时将枚举的成员值跟其他类型的值关联存储在一起
enum Score{
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
switch score {
case let .points(i):
print(i,"points")
case let .grade(i):
print("grade",i)
}// grade A
enum Date {
case digit(year:Int,month:Int,day:Int)
case string(String)
}
var date = Date.digit(year: 2011, month: 9, day: 10)
date = .string("2011-09-10")
switch date {
case .digit(let year,let month, let day):
print(year,month,day)
case let .string(value):
print(value)
}
enum Password{
case number(Int,Int,Int,Int)
case gesture(String)
}
var pwd = Password.number(3, 5, 7, 8)
pwd = .gesture("12369")
switch pwd {
case let .number(n1, n2, n3, n4):
print("numer is ",n1,n2,n3,n4)
case let .gesture(str):
print("gesture is",str)
}
必要时let也可以改为var
1.3 原始值
- 枚举成员可以使用相同类型的默认值预先对应,这个默认值叫做:原始值
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) //♣️
注意:原始值不占用枚举变量的内存
1.4 隐式原始值
enum Direction2 : String{
case north = "north"
case south = "south"
case east = "east"
case west = "west"
} //等价于
enum Direction3 : String{
case north,south,east,west
}
print(Direction2.north.rawValue,Direction3.north.rawValue)//north north
enum Season1 : Int{
case spring,summer,autumn,winter
}
print(Season1.spring.rawValue,Season1.summer.rawValue,Season1.winter.rawValue) //0 1 2 3
enum Season2 : Int{
case spring = 1,summer = 2,autumn = 3, winter = 4
}
print(Season2.spring.rawValue,Season2.summer.rawValue,Season2.autumn.rawValue,Season2.winter.rawValue) //1 2 3 4
1.5 递归枚举
indirect enum ArithExpr1{
case number(Int)
case sum(ArithExpr1,ArithExpr1)
case difference(ArithExpr1,ArithExpr1)
}
enum ArithExpr2{
case number(Int)
indirect case sum(ArithExpr2,ArithExpr2)
indirect case difference(ArithExpr2,ArithExpr2)
}
let five = ArithExpr1.number(5)
let four = ArithExpr1.number(4)
let two = ArithExpr1.number(2)
let sum = ArithExpr1.sum(five, four)
let difference = ArithExpr1.difference(sum, two)
func Calculate(_ expr:ArithExpr1) -> 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)
1.6 MemoryLayout和内存布局
- 可以使用MemoryLayout来获取数据类型占用的内存大小
enum Password2{
case number(Int,Int,Int,Int)
case other
}
MemoryLayout<Password2>.stride //40,分配占用空间的大小
MemoryLayout<Password2>.size //33,实际用到的空间大小
MemoryLayout<Password2>.alignment //8,内存对齐参数
var pwd2 = Password2.number(9, 8, 6, 4)
pwd2 = .other
MemoryLayout.stride(ofValue: pwd2) //40
MemoryLayout.size(ofValue: pwd2) // 33
MemoryLayout.alignment(ofValue: pwd2)// 8
2. 可选项
2.1 基本概念
- 可选项,一般也叫可选类型,它允许将值设置为nil
- 在类型后面加个问号?,来定义一个可选项
var name:String? = "jack"
name = nil
var age:Int? //默认就是nil
age = 10
age = nil
var array = [1,15,40,29]
func get(_ index:Int) -> Int?{
if index < 0 || index >= array.count {
return nil
}
return array[index]
}
print(get(1)) //Optional(15)
print(get(-1)) //nil
print(get(4)) //nil
2.2 强制解包
- 可选项是对其他类型的一层包装,可以将它理解为是一个盒子
- 如果为nil,那么它是一个空盒子;如果不为nil,那么盒子里装是:被包装类型的数据
var age2:Int? //默认就是nil
age2 = 10
age2 = nil
- 如果要从可选项中取出被包装的数据(将盒子里装的东西取出来),需要使用感叹号!进行强制解包
var age3:Int? = 10
var ageInt:Int = age3!
ageInt += 10
- 如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误.Fatal error: Unexpectedly found nil while unwrapping an Optional value
var age4:Int?
age4!
- 判断可选项是否包含值
let number = Int("123")
if number != nil{
print("字符串转换整数成功:\(number!)")
}else{
print("字符串转换整数失败")
}//字符串转换整数成功:123
2.3 可选项绑定
- 可以使用可选项绑定来判断可选项是否包含值。如果包含就自动解包,把赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false
if let number = Int("123"){
print("字符串转换整数成功:\(number)")
//number是强制解包之后的Int值,number的作用域仅限于这个大括号
}else{
print("字符串转换整数失败")
}//字符串转换整数成功:123
enum Season:Int{
case spring = 1, sumer,autumn,winter
}
if let season = Season(rawValue: 6){
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")
}//4<42<100
}
}
if let first = Int("4"),
let second = Int("42"),
first<second && second < 100
{
print("\(first)<\(second)<100")
}//4<42<100
2.4 while循环中使用可选项绑定
- 遍历数组,将遇到的正数都加起来,如果遇到负数或者非数字,停止遍历
var strs = ["10","20","abc","-20","30"]
var index = 0
var sum = 0
while let num = Int(strs[index]),num > 0 {
sum += num
index += 1
}
print(sum)
2.5 空合并运算符 ??
- a ?? b 。 a是可选项;b是可选或者非可选项;b和a的存储类型必须相同。
- 如果a不为nil,就返回a;如果a为nil,就返回b;如果b不是可选项,返回a时会自动解包
let a1:Int? = 1
let b1:Int? = 2
let c1 = a1 ?? b1 //c1是Int?,Optional(1)
let a2:Int? = nil
let b2:Int? = 2
let c2 = a2 ?? b2 //c2是Int?,Optional(2)
let a3:Int? = nil
let b3:Int? = nil
let c3 = a3 ?? b3 //c3是Int?,nil
let a4:Int? = 1
let b4:Int = 2
let c4 = a4 ?? b4 //c4是Int,1
let a5:Int? = nil
let b5:Int = 2
let c5 = a5 ?? b5 //c是Int,2
let a6:Int? = nil
let b6:Int = 2
//如果不使用??运算符
let c6:Int
if let tmp = a6 {
c6 = tmp
}else{
c6 = b6
}
- 多个??一起使用
let a7:Int? = 1
let b7:Int? = 2
let c7 = a7 ?? b7 ?? 3 //c7是Int,1
let a8:Int? = nil
let b8:Int? = 2
let c8 = a8 ?? b8 ?? 3 //c8是Int,2
2.6 ??和if else配合使用
let a9:Int? = nil
let b9:Int? = 2
if let c9 = a9 ?? b9 {
print(c9)
}//类似于 if a9 != nil || b9 != nil
if let c9 = a9,let d9 = b9 {
print(c9)
print(d9)
}//类似于 if a9 != nil && b9 != nil
2.7 if和guard语句实现登录
- if语句实现登录
func login(_ info:[String:String]){
let userName:String
if let tmp = info["userNam"] {
userName = tmp
}else{
print("请输入用户名")
return
}
let password:String
if let tmp = info["password"] {
password = tmp
}else{
print("请输入密码")
return
}
//if userName... ; if password ...
print("userName:\(userName),password:\(password),登录中")
}
login(["userName":"jack"]) //请输入密码
- guard实现登录
当guard语句的条件为false时,就会执行大括号里面的代码;当guard语句的条件为true时,就会跳过guard语句;guard语句特别适合用来“提前退出”;当使用guard语句来进行可选项绑定时,绑定的常量(let),变量(var)也能在外层作用域中使用。
guard 条件 else {
do something
退出当前作用域
return, break,continue,throw error
}
func login(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:\(userName),password:\(password),登录中")
}
2.8 隐式解包
- 在某些情况下,可选项一旦被设定值之后,就会一直拥有值
- 在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
- 可以在类型后面加个感叹号!定义一个 隐式解包的可选项
let num1:Int! = 10
let num2:Int = num1
if num1 != nil {
print(num1 + 6)//16
}
if let num3 = num1 {
print(num3)
}
2.9 字符串插值
- 可选项在字符串插值或者直接打印时,编译器会发出警告
var age4:Int? = 10
print("My age is \(age4)")
- 至少有三种方法可以消除警告
print("My age is \(age4!)")
print("My age is \(String(describing: age4))")
print("My age is \(age4 ?? 0)")
2.10 多重可选项
var tmp1:Int? = 10
var tmp2:Int?? = tmp1
var tmp3:Int?? = 10
print(tmp2 == tmp3) //true
var temp1:Int? = nil
var temp2:Int?? = temp1
var temp3:Int?? = nil
print(temp2 == temp3) //false
(temp2 ?? 1) ?? 2 //2
(temp3 ?? 1) ?? 2 //1
可以使用LLDB指令 frame variable -R 或者 fr v -R 来查看区别