swift中!和?區別


首先貼cocoachina上某位大大的帖子:

【轉】Swift之?和!   

 
 
Swift語言使用var定義變量,但和別的語言不同,Swift里不會自動給變量賦初始值,也就是說變量不會有默認值,所以要求使用變量之前必須要對其初始化。如果在使用變量之前不進行初始化就會報錯:
?
1
2
3
4
5
var stringValue : String  
//error: variable 'stringValue' used before being initialized
//let hashValue = stringValue.hashValue
//                            ^
let hashValue = stringValue.hashValue

上面了解到的是普通值,接下來Optional值要上場了。Optional其實是個enum,里面有None和Some兩種類型。其實所謂的nil就是Optional.None, 非nil就是Optional.Some, 然后會通過Some(T)包裝(wrap)原始值,這也是為什么在使用Optional的時候要拆包(從enum里取出來原始值)的原因, 也是PlayGround會把Optional值顯示為類似{Some "hello world"}的原因,這里是enum Optional的定義:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
enum Optional<T> : LogicValue, Reflectable {
     case None
     case Some(T)
     init()
     init(_ some: T)
  
     /// Allow use in a Boolean context.
     func getLogicValue() -> Bool
  
     /// Haskell's fmap, which was mis-named
     func map<U>(f: (T) -> U) -> U?
     func getMirror() -> Mirror
}

聲明為Optional只需要在類型后面緊跟一個?即可。如:
?
1
var strValue : String?  

一旦聲明為Optional的,如果不顯式的賦值就會有個默認值nil。判斷一個Optional的值是否有值,可以用if來判斷:
?
1
2
3
if strValue {
     //do sth with strValue
}

然后怎么使用Optional值呢?文檔中也有提到說,在使用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字符串的哈希值

到這里我們看到了?的兩種使用場景:

1.聲明Optional值變量
2.用在對Optional值操作中,用來判斷是否能響應后面的操作

另外,對於Optional值,不能直接進行操作,否則會報錯:
?
1
2
3
4
5
//error: 'String?' does not have a member named 'hashValue'
//let hashValue = strValue.hashValue
//                ^        ~~~~~~~~~
  
let hashValue = strValue.hashValue

上面提到Optional值需要拆包(unwrap)后才能得到原來值,然后才能對其操作,那怎么來拆包呢?拆包提到了幾種方法,一種是Optional Binding, 比如:
?
1
2
3
if let str = strValue {
     let hashValue = str.hashValue
}

還有一種是在具體的操作前添加!符號,好吧,這又是什么詭異的語法?!

直接上例子,strValue是Optional的String:
?
1
let hashValue = strValue!.hashValue

這里的!表示“我確定這里的的strValue一定是非nil的,盡情調用吧” ,比如這種情況:
?
1
2
3
if strValue {
     let hashValue = strValue!.hashValue
}

{}里的strValue一定是非nil的,所以就能直接加上!,強制拆包(unwrap)並執行后面的操作。 當然如果不加判斷,strValue不小心為nil的話,就會出錯,crash掉。

考慮下這一種情況,我們有一個自定義的MyViewController類,類中有一個屬性是myLabel,myLabel是在viewDidLoad中進行初始化。因為是在viewDidLoad中初始化,所以不能直接聲明為普通值:var myLabel : UILabel,因為非Optional的變量必須在聲明時或者構造器中進行初始化,但我們是想在viewDidLoad中初始化,所以就只能聲明為Optional:var myLabel: UILabel?, 雖然我們確定在viewDidLoad中會初始化,並且在ViewController的生命周期內不會置為nil,但是在對myLabel操作時,每次依然要加上!來強制拆包(?也OK),比如:
?
1
2
3
myLabel!.text = "text"
myLabel!.frame = CGRectMake(0, 0, 10, 10)
...

對於這種類型的值,我們可以直接這么聲明:var myLabel: UILabel!, 果然是高(hao)大(fu)上(za)的語法!, 這種是特殊的Optional,稱為Implicitly Unwrapped Optionals, 直譯就是隱式拆包的Optional,就等於說你每次對這種類型的值操作時,都會自動在操作前補上一個!進行拆包,然后在執行后面的操作,當然如果該值是nil,也一樣會報錯crash掉。

那么!大概也有兩種使用場景

1.強制對Optional值進行拆包(unwrap)
2.聲明Implicitly Unwrapped Optionals值,一般用於類中的屬性

 

 

swift中的?和!使用起來其實並不像大家所說的那么費勁,簡單理解主要包括以下幾個要點:

1,如果你使用?就表面你可以允許你參數賦值為nil。這個時候在使用該參數用於賦值等操作的時候必須加上!或者是加入官方說明的if判斷

    func testStr(){

        var str:String? = "hello"

        //var result = str + "world"//編譯錯誤,這個地方必須要加上!因為str可能是nil,如果nil的話下面的語句是不通的

        var result = str! + "world"//如果要使用str,要加入!,確保編譯通過

        str = nil

        //result = str! + "world" //運行錯誤,因為?表示該參數可能為nil,這個時候,程序是可以進行賦值為nil操作的,所以在做操作的時候,需要做判斷處理

        if let isnull=str{

            println("str is not null");

        }else{

            println("str is null");

        }

    }

結果:str is null

2,!表示你定義的參數是不為null的。這個時候,雖然可以進行賦值為nil的操作,但是一旦你進行了賦值nil操作是編譯不過的

   func testStr(){

        var str:String! = "hello"

        var result = str + "world"//沒有問題,這個地方因為定義的是!,所以str肯定不為空,改語句成立

        

        str = nil

        result = str + "world"//編譯通過,但是運行時出錯

        result = str! + "world"//編譯通過,但是運行時出錯。

        if let isnull=str{

            println("str is not null");

        }else{

            println("str is null");

        }

    }

3,如果不適用!和?操作

    func testStr(){

        var str:String = "hello"

        var result = str + "world"//沒有問題

        

        //str = nil  //這個地方是不可以進行賦值為nil操作的。

        //result = str? + "world"//編譯不通過

        //result = str! + "world"//編譯不通過

 

        iflet isnull=str{// 這個地方就不能這樣用了,因為這種用法只使用於Optional type

            println("str is not null");

        }else{

            println("str is null");

        }

    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM