斯威夫特的多种Optional

图片 1

前言

Optional是斯威夫特的多个特色,它解决了“有”和“无”那七个麻烦了Objective-C许久的军事学概念,同时期码安全性也获得了异常的大的加码,可是与之带来的困难之处就是本人前几天要和大家一起念书的多重Optional。

 

什么是Optional

学学多种Optional此前,咱们先轻易的问询一下Optional,大家能够见见Optional实际上正是二个enum:

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    case None
    case Some(Wrapped)
    /// Construct a `nil` instance.
    public init()
    /// Construct a non-`nil` instance that stores `some`.
    public init(_ some: Wrapped)
    /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
    @warn_unused_result
    public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
    /// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
    @warn_unused_result
    public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
    /// Create an instance initialized with `nil`.
    public init(nilLiteral: ())
}

在概念中,对<Wrapped>木有任何限制,换言之,就是我们能够在Optional中装入任性的事物,包罗Optional本人。要是大家把Optional比作三个盒子,具体的String
只怕Int那样的值大家比作糖果,当大家打开盒子的时候,或许的结果有:空气,糖果,另2个盒子。
比如:

var string: String? = "string"
var anotherString: String?? = string

string 是Optional<String>,
anotherString是Optional<Optional<String>>。
本人的精通是这么的:string好比是2个盒子,展开盒子是二个字符串string。anotherString也好比是多少个盒子(一号盒子),打开盒子(1号盒子)看见的是另3个盒子(贰号盒子),再打开盒子(贰号盒子)是3个字符串string。除开将一个Optional赋值给多种Optional以外,这里我们也足以将一贯的字面量赋值给它:

var literalOptional: String?? = "string"

上边那几个例子辛亏,依照项目标推理大家不得不将Optional<String>放入到literalOptional中,能够猜测它和anotherString是壹致的。可是有种新鲜的意况便是只要大家将nil赋值给它的话,情状就和方面不雷同了,举个例子说:

var aNil:String? = nil
var anotherNil: String?? = aNil
var literaNil: String?? = nil

本人的明亮是那般的:aNil好比是贰个盒子,展开盒子开采中间什么都未曾,是空气。anotherNil也好比是一个盒子(壹号盒子)),张开盒子(一号盒子)看见是另三个盒子(贰号盒子),再张开盒子(二号盒子)发掘里面什么都木有是空气。上边我们估算literalOptional和anotherString是1模同样的,那这里的literaNil和anotherNil是或不是如出一辙的吧?从地点作者的描述中也可以精晓是差别等的。有点像俄罗斯套娃同样,3个是开拓开采里头什么都并未有,另3个是张开了中间还有三个套娃(只不过那个套娃里面木有东西)。若是我们还不能通晓的话,大家就上代码吧:

if let a = anotherNil {
    print("anotherNil")
}
if let b = literaNil {
    print("literaNil")
}
//控制台只会输出anotherNil

壹旦大家用playground运维以来,看起来会特别有利于,左边的垄断(monopoly)台会直接打字与印刷Optional的值,能够辅助大家领略。

 

 

拆包和平消除包的原因:

  其实所谓的 nil 就是 Optional.None, 非 nil 正是Optional.Some,
然后会通过Some(T)包装(wrap)原始值,那也是为什么在利用 Optional
的时候要拆包(从 enum 里收取来原始值)的原委, 也是 PlayGround 会把
Optional 值彰显为接近 {Some “hello world”}的原因.

1.swift
?和 !的区别

  一.1 斯威夫特语言使用var定义变量,但和别的语言分裂,Swift里不会自动给变量赋早先值,也正是说变量不会有暗中认可值,所以须要选拔变量在此之前必须求对其初叶化。假若在使用变量从前不开始展览初始化就能够报错:

1 var stringValue : String 
2 //error: variable 'stringValue' used before being initialized
3 //let hashValue = stringValue.hashValue
4 let hashValue = stringValue.hashValue

  若在3个视图调整器中 class xxxController

 1 class AdjustFontViewController: UIViewController{    //报错error: Class 'xxxx' has no initializers  
 2 //Fix-it Stored property 'stringValue' without initial value prevents synthesized initalizers
 3 
 4 var stringValue: String
 5 
 6 }
 7 
 8 //    修改为:
 9 //    当我们不知道变量的值时,可以将改变量设置为可选类型
10 var stringValue: String?  
11 
12 
13 
14 //    表明str是可选类型(是string类型或者nil类型)  
15 var value: String?="hello world"  
16 //value = value?.uppercaseString  
17         
18 //    拆包  
19       if let unwrappedStr = value{  
20           print("拆包:\(unwrappedStr.uppercaseString)")     //如果value(有值,将值复制给常量unwrappedStr)  
21       }  
22       else  
23       {  
24           print("value是nil")    //str不赋初始值时  
25       }  
26        
27 //    确定value有存在的值(如果str没有被赋实际意义的值,强制拆包将造成应用程序崩溃)  
28       print(" 拆包:\(str!.uppercaseString)")  
29  
30 
31 
32 //    两种拆包方式       
33 //    1.隐性拆包  
34       var str1:String! = "hello world"    //已经确定变量str1是有值的(如果未赋值,应用程序将崩溃)  
35       str1 = str1.lowercaseString  
36       print("拆包:\(str1)")  
37         
38       print(str)  
39         
40 //    2.显性拆包
41       var str2:String?="Hello World"  
42       let lowerStr2 = str2?.lowercaseString//lowerStr2是可选的,如果有值,则值为“hello world”否则为nil 
43 
44 
45 
46 
47                       

 

 

 

  一.2 上边领会到的是常见值,接下去Optional值要进场了。

    Optional其实是个enum,里面有None和Some两种档期的顺序。其实所谓的nil正是Optional.None,
非nil正是Optional.Some,
然后会通过Some(T)包装(wrap)原始值,这也是怎么在行使Optional的时候要拆包(从enum里抽出来原始值)的案由,
也是PlayGround会把Optional值突显为接近{Some “hello
world”}的原故,这里是enum Optional的概念:

 1 enum Optional<T> : LogicValue, Reflectable {
 2     case None
 3     case Some(T)
 4     init()
 5     init(_ some: T)
 6 
 7     /// Allow use in a Boolean context.
 8     func getLogicValue() -> Bool
 9 
10     /// Haskell's fmap, which was mis-named
11     func map<U>(f: (T) -> U) -> U?
12     func getMirror() -> Mirror
13 }

一.2.一  ?的两种采用情况:

a. 评释 Optional 值为可选变量

b. 用在对 Optional 值操作中,用来判别是或不是能响应前面 的操作

c. 用于安全调用 protocol 的 optional 方法

d. 使用 as? 向下转型(Downcast)

 

a. Optional 可选值 

 定义变量时,假设钦赐是可选的,表示该变量能够有一个点名项目的值,也足以是
nil

 定义变量时,在品种后边增多1个 ?,表示该变量是可选的

 变量可采取的暗许值是 nil

 1 //n1 = nil  //编译错误
 2 
 3 //let str: String = nil   //编译错误
 4   
 5    
 6  var n2 : Int? = 10
 7  
 8  print("-----n2=\(String(describing: n2))")
 9  
10  n2 = nil
11  
12  print("-----n2=\(String(describing: n2))")
13 
14 
15  let str: String! = nil
16 
17  print("-----str=\(str)")
18  

 

 常量可挑选未有私下认可值,首要用于在构造函数中给常量设置开始数值

扬言为Optional只须求在品种后边紧跟3个?就能够,strValue 是 Optional 的 String。如:

1 var strValue: String?   //?相当于下面这种写法的语法糖
2 var strValue: Optional<Int>

若是申明为Optional的,借使不显式的赋值就能够有个私下认可值nil。判别三个Optional的值是或不是有值,能够用if来决断:

if strValue {
    //do sth with strValue
}

 

1.二.2  展现拆包和隐式拆包

 

a.
 使用问号(?)表明的可选类型,在拆包时需求选拔感叹号(!),那种拆包方式叫做“显式拆包”;

 

b.
使用惊讶号(!)申明的可选类型,在拆包时能够不行使惊讶号(!),那种代表方法叫做“隐式拆包”。

 

 1  
 2 
 3 
 4 //  在可选类型的问号(?)或感叹号(!)究竟有什么区别呢?这与可选类型的“拆包”(unwrapping)有关,拆包是将可选类型变成普通类型,如果我们直接打印非空的可选类型值,代码如下:
 5 
 6 var n3: Int? = 10
 7 
 8 print(n3)   //输出是:Optional(10)
 9 
10 //print(n3 + 10)     //发生编译错误,加 !可以编译过
11 
12  
13 
14 /* 
15 
16 如果 Optional 值是 nil,不允许参与计算
17 
18  只有解包(unwrap)后才能参与计算
19 
20  在变量后添加一个 !,可以强行解包
21 
22  */
23 
24 var n4: Int? = 10
25 
26 print(n4! + 100)    //显式拆包
27 
28  
29 
30 var n5: Int! = 10
31 
32 print(n5 + 200)  //隐式拆包
33 
34  

 

 

1.3  ??运算符

 

 1 /*
 2 
 3 ?? 运算符可以用于判断 变量/常量 的数值是否是 nil,如果是则使用后面的值替代
 4 
 5  在使用 Swift 开发时,?? 能够简化代码的编写
 6 
 7 */
 8 
 9 
10 let num: Int? = nil
11 
12  
13 let r1 = (num ?? 0) + 10
14 
15 print(r1)

 

 

一.四 拆包判别

   单个可挑选剖断

 

 1  let url = NSURL(string: "http://www.baidu.com")
 2 
 3  
 4 
 5  //: 方法1: 强行解包 - 缺陷,如果 url 为空,运行时会崩溃
 6 
 7  let request = NSURLRequest(URL: url!)
 8 
 9  
10 
11  //: 方法2: 首先判断 - 代码中仍然需要使用 `!` 强行解包
12 
13  if url != nil {
14 
15  let request = NSURLRequest(URL: url!)}
16 
17  
18 
19  //: 方法3: 使用 `if let`,这种方式,表明一旦进入if 分支,u 就不在是可选项
20 
21  if let u = url where u.host == "www.baidu.com" {
22 
23  let request = NSURLRequest(URL: u)
24 
25  }

 

 

 一.四.2  可选项标准判别

    1> 初学 swift 壹非常的大心就能够让 if 的嵌套档期的顺序很深,让代码变得极丑

 

1 if let u == url
2 
3 {
4 
5     if u.host == "www.baidu.com" {
6 
7         let request = NSURLRequest(URL: u)
8 
9 }

 

    2> 使用 where 关键字

小心:if let 不能够与利用 &&、|| 等条件判别。假若要扩张条件,能够动用
where 子句,where 子句未有智能提示

 

1  if let u = url where u.host == "www.baidu.com"     {
2 
3  let request = NSURLRequest(URL: u)
4 
5  }

 

 

 

文书档案中也有涉及说,在行使Optional值的时候必要在实际的操作,比如调用方法、属性、下标索引等前面须求加上四个?,经喵神指正,”Optional
Chaining的问号的乐趣是询问是还是不是响应后边那一个办法,和原来的isResponseToSelector有些类似”,假诺是nil值,也等于Optional.None,纵然无法响应前面包车型大巴章程,所以就能跳过,如若有值,正是Optional.Some,只怕就能够拆包(unwrap),然后对拆包后的值实行后边的操作,比如:

1 let hashValue = strValue?.hashValue

strValue是Optional的字符串,假设strValue是nil,则hashValue也为nil,假使strValue不为nil,hashValue便是strValue字符串的哈希值

到此处我们看看了?的两种选拔情状:

声明Optional值变量
用在对Optional值操作中,用来判定是或不是能响应前面包车型客车操作

除此以外,对于Optional值,无法直接实行操作,不然会报错:

1 //error: 'String?' does not have a member named 'hashValue'
2 //let hashValue = strValue.hashValue
3 //                ^        ~~~~~~~~~

上面提到Optional值要求拆包(unwrap)后技巧博得原来值,然后才能对其操作,那怎么来拆包呢?拆包提到了二种方法,1种是Optional
Binding, 比方:

1 if let str = strValue {
2     let hashValue = str.hashValue
3 }

 

 1 func demo4() {
 2 
 3     let urlString = "http://www.baidu.com/中文"
 4 
 5     // 注意:构造函数如果有 `?` 表示不一定能够创建出对象
 6 
 7     let url = NSURL(string: urlString)
 8 
 9     print(url)
10 
11     
12 
13     // 注意:如果参数中,没有 ? 表示必须要有值,如果为 nil,就崩!
14 
15     if url != nil {
16 
17         let request = NSURLRequest(URL: url!)
18 
19         print(request)
20 
21     }
22 
23 }

 

  壹.2 加惊讶号    

    1.二.壹 ! 使用的风貌

  a. 强制对 Optional 值实行拆包(unwrap)

  b. 表明 Implicitly Unwrapped Optionals 值,一般用于类中的属性

 

例子,strValue是Optional的String:

1 1 let hashValue = strValue!.hashValue  //这里的!表示“我确定这里的的strValue一定是非nil的,尽情调用吧”

{}里的strValue一定是非nil的,所以就会直接助长!,强制拆包(unwrap)并施行前面包车型大巴操作。
当然假若不加判别,strValue相当大心为nil的话,就能够出错,crash掉。

 

考虑下这一种状态,大家有二个自定义的MyViewController类,类中有贰性子能是myLabel,myLabel是在viewDidLoad中开始展览早先化。因为是在viewDidLoad中伊始化,所以无法一向注明为普通值:var
myLabel :
UILabel,因为非Optional的变量必须在宣称时也许构造器中开始展览起始化,但我们是想在viewDidLoad中初始化,所以就不得不表明为Optional:var
myLabel: UILabel?,
尽管大家显然在viewDidLoad中会初步化,并且在ViewController的生命周期内不会置为nil,可是在对myLabel操作时,每一回照旧要加上!来强制拆包(在读取值的时候,也足以用?,谢谢iPresent在回复中唤醒),比如:

1 myLabel!.text = "text"
2 myLabel!.frame = CGRectMake(0, 0, 10, 10)

对此那类别型的值,大家得以一贯这么申明:var myLabel: UILabel!
,那种是卓殊的Optional,称为Implicitly Unwrapped Optionals,
直译便是隐式拆包的Optional,就10分说您每一回对那种类型的值操作时,都会活动在操作前补上四个!举行拆包,然后在实施后面包车型客车操作,当然要是该值是nil,也同等会报错crash掉。

1 var myLabel: UILabel!  //!相当于下面这种写法的语法糖
2 var myLabel: ImplicitlyUnwrappedOptional<UILabel>

 

进展:对 !应用还有 as

那么!大概也有二种选择处境
一.强制对Optional值进行拆包(unwrap)
贰.申明Implicitly Unwrapped Optionals值,一般用于类中的属性

 

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图