如何理解Swift中Optional的 ! 和 ?

很多人在刚上手swift时对于Optional中 ‘!’ 和 ‘?’ 的使用十分不理解,接下来我会谈一谈自己对于这两个符号的使用方式的理解。

先来说说Optional的概念,以方便之后的理解。来看一下下面的代码:

var a : String = nil // 编译错误,String类型不能为nil
var b : String = “Hello!”
1
2
Swift 中的普通类型不再能设为nil。那如何表示这个值不存在呢?所以引进了Optional的概念:代表 nil 或某个具体的值。例如:

var c : String? = nil
var d : String? = “Hello!”
1
2
String? 就是一个Optional,它既能够被具体类型赋值, 也可以赋值为nil。通过 String 和 Optional的比较,发现Optional 就相当于把具体类型和 nil 打包捆绑在了一起,转变成了一种新的类型。

Optional 有两种声明方法:

var apple: String?
var bread: String!
1
2
很多人认为,声明为 String! 的变量表明该变量的值不为 nil。但是,实际上,String! 和 String? 都是有默认值的,且默认值都为nil。我们为它们赋一个初值再打印类型来看看:

var apple: String? = “apple”
var bread: String! = “bread”
1
2
(lldb) p apple
(String?) $R0 = “apple”

(lldb) p bread
(String?) $R1 = “bread”

发现,尽管apple 和 bread 虽然一个是 String? 一个是 String! 但是打印出来都是 String? 类型的。所以更恰当的理解应该是:String! 只是理解意义上的不为nil,其本质还是一个 Optional,从声明来说它和 String? 完全等价,所以也能够赋值为 nil 。

为了之后叙述方便,我们把定义类似 String? 的称为 Optional(www.chonylpt.com?), 定义类似 String! 的称为 Optional(!)。

Optional 的本质是囊括 nil 和具体类型的一种枚举。获取它具体值的操作过程称之为拆包,用 “!” 表示。先来做个实验:

var string : String
var optionalString: String?
var nonOptionalString: String!

string = nonOptionalString // ① 崩溃
string = optionalString // ② 编译错啦
string = optionalString! // ③ 崩溃
具体看一下报错原因:
① String! 赋值给 String ,报错为:拆包时Optional的值为nil;
② String? 赋值给 String,编译器报错为 Optional未拆包;
③ 拆包后的 String? 赋值给 String,这时,报错为:拆包时Optional的值为nil;
String! 和 拆包后的 String? 进行传值时报的错相同,由此我们可以得出一个结论:编译器默认 Optional(!) 是确定有值的,所以不需要再对 nil 的情况进行处理。Optional(!) 的变量只是给予了自动拆包的权限,在实际使用它的过程中不需要再次使用 ‘!’ 进行拆包。在swift官方文档中称为:Implicitly Unwrapped Optional (隐式拆包),也可以理解成“拆过包了”。

但是,只有在变量确认有值的情况下才能进行拆包。就像如上代码,optionalString 为nil,进行拆包就会引发崩溃。swift官方建议,Optional 使用 if + ! 结合的方式或者 if let 进行安全地拆包。简单的看下代码:

if optionalString != nil {
append(string: www.078881.cn/ optionalString!)
} else {
// do something

if let actualString = optionalString {
append(string: actualString)
} else {
// do something
再次提醒一下,Optional(!) 和 www.yongshiyule178.com Optional(?) 都可以使用这两种方式进行安全地拆包,只是编译器不会对没有处理 nil 情况的 Optional(!) 报错。

下图展示了 Optional 和 String 可接收类型的比较:

拆完包之后的 Optional 其实就是 String 类型。编译器强制使用者在变量为 nil 的时候要进行处理,否则就会报错会崩溃。String! 是为了规避变量一定不为 nil 的情况下却要反复判断是否为 nil 的冗余代码而产生的。例如,我们在使用 IBOutlet 时,一定会定义成 Optiona(!)。String! 在声明时和 String? 完全等价,在使用时和 String 完全等价。

总结一下:

Optional的本质是一个包含了 nil 和普通类型的枚举,这是为了确保使用者在变量为 nil 的情况下会完成相应的处理;
无论是 Optional(!) 还是 Optional(?) 都是一种Optional,在未设初始值时,默认为nil。Optional(!) 只是给予了自动拆包的权限,省略了判断其值是否为nil的过程,但是不能够保证它的值不为nil;
Optional(!) 在声明时和 Optional(?) 等价,在使用时和具体类型等价;
一定要确保 Optional 不为 nil 的情况下才可直接拆包,不然会引发崩溃。

时间: 2024-10-05 05:58:40

如何理解Swift中Optional的 ! 和 ?的相关文章

swift中Optional用法以及常问问题

很多同学问Optional中?和 !区别,弄清这个问题我们首先了解一下swift中Optional语法 Swift语言使用var定义变量,但和别的语言不同,Swift里不会自动给变量赋初始值,也就是说变量不会有默认值,所以要求使用变量之前必须要对其初始化.如果在使用变量之前不进行初始化就会报错: var stringValue : String  //error: variable 'stringValue' used before being initialized //let hashVal

[翻译]理解Swift中的Optional

原文出处:Understanding Optionals in Swift 苹果新的Swift编程语言带来了一些新的技巧,能使软件开发比以往更方便.更安全.然而,一个很有力的特性Optional,在你第一次使用时可能会感到困惑.Optionals将会在编译阶段检查哪些值为nil.通过这种方式,你可以更好的保证应用程序交付在用户手里是可运行的.在Swift中,Optionals也提供了一些接口用来和遗留的Objective-C代码之间交互. 初试Optional 让我们在XCode中新建一个叫做s

理解Swift中map 和 flatMap对集合的作用

map和flatMap是函数式编程中常见的概念,python等语言中都有.借助于 map和flapMap 函数可以非常轻易地将数组转换成另外一个新数组. map函数可以被数组调用,它接受一个闭包作为參数,作用于数组中的每一个元素.闭包返回一个变换后的元素.接着将全部这些变换后的元素组成一个新的数组. 简单的说. map就是映射函数,把一个集合映射成还有一个集合. Swift的flatMap不easy理解,flatMap非常像map函数,可是它摒弃了那些值为nil的元素. flatMap是在处理一

swift中的optional

optional一些理解和总结: Optional作为一种类型,既可以存储一个值,也可以为空(也就是swift里的nil): 其实为一个枚举类型,包含nil或者一个值,如下: enum Optional<T>: _Reflectable, NilLiteralConvertible { case None case Some(T) //... } 它只有两种状态,包含一个值或者为空:optional有效解决了值为空的清空:我们可以通过判断一个对象是否为空了做出相应解决,通过if let a =

Swift中的Optional类型 (可选类型)与强制解包 ? !

我们在swift的开发中会经常遇见?和! ,理解这两个符号深层次的内容对我们的开发是相当有利的: 目前网上对swift3.0的教程还相当的少,如果去搜索会发现早期的说法,在定义变量的时候,swift是不会给变量赋值初始值的,所以当你声明一个字符串变量的时候,var string:String   就会被系统报错,但是目前的swift版本却不报错了. 但是你要给一个字符串赋值未nil的时候,var string:String = nil 就一定是错误的   因为nil并不是一个字符串类型,而实际上

swift中构造方法和Kvc

一.引言 构造方法是一个类创建对象最先也是必须调用的方法,在Objective-C中,开发者更习惯称这类方法为初始化方法.在Objective-C中的初始化方法与普通函数相比除了要以init抬头外并无太严格的分界,而在Swift语言体系中,构造方法与普通的方法分界十分严格,从格式写法上就有不同,普通方法函数要以func声明,构造方法统一为init命名,不需要func关键字声明,不同的构造方法采用方法重载的方式创建. 二.构造方法的复写与重载 在Objective-C中,不同的初始化方法就是不同的

Swift 中枚举

Swift 中枚举高级用法及实践 字数11017 阅读479 评论0 喜欢20 title: "Swift 中枚举高级用法及实践"date: 2015-11-20tags: [APPVENTURE]categories: [Swift 进阶]permalink: advanced-practical-enum-examples 原文链接=http://appventure.me/2015/10/17/advanced-practical-enum-examples/作者=Benedik

swift中Any,AnyObject,AnyClass的区别

这几个概念让人很迷惑,看了很多帖子,终于搞明白了,简单总结: Any 和 AnyObject 是 Swift 中两个妥协的产物.什么意思呢,oc中有个id关键字,表示任何对象,oc和swift混编的时候拿什么对应id呢?就发明出来了AnyObject.但是!oc中的NSString,NSArray等都是class,但是在swift中String,Array都是struct,这个怎么办呢,混编的时候NSString等类型对应的id就不能用AnyObject了,为了填坑,又搞出来个Any,表示任意类

浅谈 Swift 中的 Optionals

input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,input[type="time"].input-sm,.form-horizontal .form-group-sm input