Swift 可选型
1、可选型
Swift 语言为我们提供了一种全新的、更加安全的类型 “可选型”。可选型是使用范型枚举的形式来组织的,也就是说此特性可以运用于所有的类型、结构体、类或者其他复杂数据类型。
- Swift 中的可选型会在编译时就去检查某些可能发生在运行时的错误,可以提高代码的质量。
- 可选型用于某些不确定是否有值的情况,其有两个值,即 “具体的值” 和
nil
。 - 当一个变量、常量或者其他类中存储有值的时候返回里面存储的值,没有值的时候返回
nil
(表示空值)。 - Swift 中把一个变量定义为可选型,在没有赋值的情况下它会被默认赋值为
nil
。 - 在 OC 中
nil
表示的是一个指向不存在对象的指针,而 Swift 中表示空的关键字为nil
,没有其他含义。 - 在 Swift 中不能给一个可选型之外的类型赋值为
nil
。
可选的声明
可选的标准声明形式,直接在程序中使用变量类型紧跟
?
var age: Int?
可选的显式声明形式
var age: Optional<Int>
相比单纯的
nil
,使用可选型封装的nil
给了开发者更合理的机会去处理空值:选择忽略空值,或者为可能出现空值的对象赋上默认值(使用??
操作符)。在条件足够完备的时候,尽量不要定义一个可选型,比如网络通信是一个复杂的环境,很多因素是不可控的,所以如果程序中需要通过网络采集数据,那么数据的借口可能是可选型的,最好不要把你的数据模型也声明成可选型,虽然这样可以减少网络数据到本地数据映射时的代码量。更好的方法是对可选型使用
??
赋值,这样在网络数据返回空值的时候,你的数据模型对应的属性就是初始化时设置的默认值。
2、强制解包可选
Swift 中把一个变量定义为可选型,在没有赋值的情况下它会被默认赋值为
nil
,所以即便定义了 age 的时候不赋值,它也已经被初始化了,它的值是nil
,直到你通过赋值更改它的值。var age: Int?
age = Int("12")
print("age is \(age)")
- 在输出语句中我们得到 age 的值显示为
Optional(12)
,Optional
代表 “可选”,age 的当前值为一个整数类型的可选型。 - 在实际开发中我们真正需要的是括号中的 12,要想获得这个 12,就需要使用 “解包” 操作。
- 在输出语句中我们得到 age 的值显示为
解包是针对于可选类型的变量操作,当我们确定一个可选类型的值不为
nil
的时候,可以使用变量名紧跟!
的方式强制获取类型可选中的值,从而省略判断步骤,这种机制叫做 “强制解包可选”。var age: Int?
age = Int("12")
print("age is \(age!)")
- 此时在输出语句中我们得到 age 的值显示为 12。
但是如果这个变量中没有值,那么在解包的时候程序就会崩溃。
3、可选绑定
想比于强制解包,Swift 提供了一种更安全的解包方式 “可选绑定”。可选绑定有两种格式
if-let
和guard-let-else
。可选绑定本身代表了两个步骤:解包和拷贝,如果你需要一个解包后的变量拷贝的话,可以使用
if-var
(guard-var
)的语法。let firstStr: String? = "hello"
let secondStr: String? = "world" if var fs = firstStr, let ss = secondStr { print(fs + " " + ss) // hello world fs = "hi" // 修改 print(fs + " " + ss) // hi world
}
3.1 if-let 格式
if-let
格式var age: Int? if let value = age { // 在内部使用解包后的值
print("\(value)")
}
- 如果 age 有值,对 age 解包并给其一个 “别名”
value
,在if
后的大括号对中vaule
才有效果。 - 如果 age 无值,即为
nil
时,那么大括号对中的代码不会执行,使可选解包程序不会崩溃。
- 如果 age 有值,对 age 解包并给其一个 “别名”
3.2 guard-let-else 格式
guard-let-else
格式var age: Int? guard let value = age else { // 终止方法
return
} // 在外部使用解包后的值
print("\(value)")
guard-let-else
首先处理 age 为nil
时的情况,在此种情况下必须在大括号中使用return
或者break
提前终止代码。- 如果 age 有值,那么 age 的值也会被保存在 “别名”
value
中,不管有多少个guard-let-else
,“别名”value
的作用域都在最外层。
3.3 可选绑定嵌套
在 Swift 1.2 之前
if let a = b { if let c = d { ...
}
}
在 Swift 1.2 之后,可以把所有的可选绑定放到一个
if-let
中,使用逗号隔开。if let a = b, c = d, ... { }
在 Swift 3.0 之后,可选绑定和普通的判断语句可以混用到一个
if
语句中,所以每一次可选绑定都需要用let
关键字来声明。if let a = b, let c = d, ... { }
4、隐式解包
隐式解包是一种特殊的可选型,在系统中由于一些历史原因出现这种类型,但是我们自己最好不要定义这样的类型。
- 将可选变量声明中的
?
改为!
来标注一个隐式解包可选。 - 隐式解包可选用于一个确定会有值的可选类型实例声明。
var age: Int! = 18
- 将可选变量声明中的
隐式解包的类型可以和普通的类型进行运算,此时它会被解包,结果返回的也是非可选型的类型。
let a: Int! = 3 let b = a + 1 // b 的类型是 Int,值为 4
在 Swift 2.2 中,隐式解包的实例传递的对象的类型依旧是隐式解包类型的,并且和普通类型所组成的数组也会被推断为隐式解包的数组类型。
let a: Int! = 3 let c = a // c 的类型依旧为 Int!
let arr = [1, a] // 数组 arr 的类型是 [Int!] 类型的
在 Swift 3.0 及以后,隐式解包的类型在传递时都会被当作普通的可选型,只有在运算这种需要解包的时候才会被解包。
let a: Int! = 3 let c = a // c 的类型依旧为 Int?
let arr = [1, a] // 数组 arr 的类型是 [Int?] 类型的
在 Swift 工程中,最常见的隐式解包就是
IBOutlet
。IBOutlet weak var dateLabel: UILabel!
IBAction
和IBOutlet
允许你的代码和 Interface Builder 中的元素进行交互,通过隐式解包的声明方式。- 当 Swift 代码对象初始化之后,storyboard 中的元素会在运行时与
IBOutlet
对象对接,这也是不能在prepareForSegue
方法中访问目标控制器中的IBOutlet
元素的原因,因为在初始化时会造成对nil
的解包导致程序崩溃。
5、可选运算符
可选运算符
??
之前是一个可选型,??
之后是一个非可选型的值。??
操作符自带解包功能,在赋值时??
会检查其之前的可选型。var age: Int?
var value = age ?? -1
- 如果可选型不为
nil
则将其解包并返回。 - 如果为
nil
,则不会返回nil
,此时返回??
之后的非可选型的值。
- 如果可选型不为
6、可选链
针对可选型结果中仍有可选型的情况,可以使用可选链操作,将可选的调用使用
?
连接在一起形成一个链,如果可选链中任何一个部位的可选型是空值,则可选链会中断,而不会引发强制解包可选时发生的错误。- 除了可以对可选型中的属性进行判空外,可选链更适用于可选型中的方法调用。
- 如果可选链末尾是一个方法,那么当链条提前中断的时候,该方法不会执行。
- 可选链可以多层可选(嵌套)。
有一个类
Student
,里面有两个属性name
和age
,它们的类型是可选型。class Student {
var name: String?
var age: Int?
} func example(code: String) -> Student? { if code == "xm" { let xiaoming: Student = Student()
xiaoming.name = "XiaoMing"
xiaoming.age = 12
return xiaoming } else if code == "xg" { let xiaogang: Student = Student()
xiaogang.name = "XiaoGang"
xiaogang.age = 13
return xiaogang
} return nil
}
// 使用可选绑定来判断
if let student = example(code: "xm") { // 使用可选绑定来判断
if let age = student.age { let year = 18 - age
print(year)
}
}
上面的代码运行没有问题,但是使用了两个可选绑定,因此可以使用一个
if let
即使用可选链来化简。// 使用可选链来判断
if let age = example(code: "xm")?.age { let year = 18 - age
print(year)
}
可选绑定与可选链的使用
- 如果需要频繁的使用一个可选型的值,那么可选绑定的代价是和算的。
- 如果需要在某个可选型有值的时候调用它内部的方法,如果是空值则不调用,那么使用可选链的方式会更加简单。
可选绑定的理念是忽略空值,而可选链中可以使用空值,比如当空值出现时使用
??
把空值转换成常量。let age = example(code: "lw")?.age ?? 0
7、Map 和 FlatMap
可选型中有两个非常实用的方法
map
和flatMap
,当你确定需要返回一个可选型,并且希望当可选型不为空时,对可选型的值做某些处理后再返回,可以选用map
。可选型的
map
和flatMap
的定义如下public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
在不确定某个学生的年龄是否有值的情况下,我们定义一个
isAdult
方法,它的作用是判断那些有年龄信息的学生的年龄是否成人,对于年龄信息未登记的学生,则返回nil
。func isAdult(stu: Student) -> Bool? { let result = stu.age.map {
$0 >= 18 ? true : false
} return result
}
let xm = Student()
xm.age = 3
xm.name = "XiaoMing" print(isAdult(stu: xm) ?? -1) // false
- 当某个学生的年龄存在时,参数的
map
方法才会执行。 - 和可选绑定一样,
map
方法体中的参数已经默认解包了,所以这里的$0
即指stu.age
解包后的值,是Int
类型的。
- 当某个学生的年龄存在时,参数的
如果使用
map
时,map
方法体中的返回值本身也是一个可选型,则返回结果会出现多重可选型。let result = example(code: "xm").map { isAdult(stu: $0)
}
- 由于在
map
方法体中的方法isAdult
返回值也是可选型的,所以result
的类型是Bool??
,在使用时我们被迫需要做两次空值判断,而这些操作并没有意义,我们关心的只是结果。
- 由于在
flatMap
会把多重可选型化简成一个可选型。let result = example(code: "xm").flatMap { isAdult(stu: $0)
}
Swift 可选型的更多相关文章
- Swift中可选型的Optional Chaining 和 Nil-Coalesce(Swift2.1)
/* 下面是介绍Optional Chaining 和 Nil-Coalesce */ // Optional Chaining (可选链) if let errorMessage = errorMe ...
- 重新认识Swift中的可选型(Swift2.1)
//: Playground - noun: a place where people can play import UIKit /* Swift中nil代表是是另外一种类型, 而不像OC那样, 任 ...
- swift 进阶笔记 (一) —— 可选型
swift定义可选型的时候,要用"?",可是在swift的标准库中,可选型的定义是Optional<T>,"? "仅仅是个简写形式. var myN ...
- swift中为什么要创造出可选型?
(1)因为nil这个东西,swift中没有就是没有. Int? 叫 整型可选型,如果不提前声明,直接赋值变量 nil会报错 . 可以将Int赋值给Int? ,但是不能将Int?赋值给Int . ...
- Swift隐式可选型简单介绍
/* 隐式可选型 */ // 隐式可选型同样可以赋值为nil, 而且在后面对这个变量的使用也可以不用进行解包 var value: String! = nil // print(value) 这行代码 ...
- Swif - 可选型
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Menlo; color: #4dbf56 } p.p2 { margin: 0.0px 0. ...
- [转载] 对象存储(2):OpenStack Swift——概念、架构与规模部署
原文: http://www.testlab.com.cn/Index/article/id/1085.html#rd?sukey=fc78a68049a14bb228cb2742bdec2b9498 ...
- 转 : React Native 开发之 IDE 选型和配置
转:https://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=2651112392&idx=1&sn=135e29ddde30 ...
- Swift - 41 - swift1.2新特性(1)
更简洁的if-let import UIKit func attack(name: String, enemyName: String, weapon: String) { print("\ ...
随机推荐
- MongoDB: 如何删除一个collection中的一个字段?
Try this: If your collection was 'example' db.example.update({}, {$unset: {words:1}}, false, true) ...
- Mathematica 文本界面获得之前的结果
使用%号做标记.获得文本界面之前的运算结果:
- java注解Annotation的使用实例
1.注解的定义 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java. ...
- slf4j、jcl、jul、log4j1、log4j2、logback大总结
1 系列目录 jdk-logging.log4j.logback日志介绍及原理 commons-logging与jdk-logging.log4j1.log4j2.logback的集成原理 slf4j ...
- HTTP协议综合
1.用浏览器模拟各种User Agent 测试页面的时候经常需要不同的User Agent,Firefox.Chrome浏览器就可以完美的模拟出各种User Agent.User Agent Swit ...
- javascript常用的公共方法
附件下载 //摘要:将指定字符串中的格式项替换为指定数组中相应对象的字符串表示形式. //参数:复合格式字符串. //返回结果:format的副本,其中的格式项已替换为 args 中相应对象的字符串表 ...
- 微信小程序 - 自定义switch切换(示例)
点击下载:switch示例 ,适用于表单,官方switch 说明 .
- To LACP or not to LACP (on a 5.1 vDS)
http://www.poppingclouds.com/2012/12/20/to-lacp-or-not-to-lacp-on-a-5-1-vds-2/ I have been recently ...
- 动态加载jar包(一)
一.编写被调用的类 package com.qunar.helloworld; public class HelloWorld { public String sayHello(){ return ( ...
- 使用MAVEN打JAR,直接使用
一.简单的方法: 首先在pom.xml里面添加: <build> <plugins> <plugin> <artifactId>maven-assemb ...