Swift語法中的“ ?” 和 ”!“區別

Swift語言使用var定義變量,但和別的語言不同,Swift裏不會自動給變量賦初始值,也就是說變量不會有默認值,所以要求使用變量之前必須要對其初始化。如果在使用變量之前不進行初始化就會報錯:

var stringValue : String 
//error: variable 'stringValue' used before being initialized
//let hashValue = stringValue.hashValue
//                            ^
let hashValue = stringValue.hashValue


上面瞭解到的是普通值,接下來Optional值要上場了。經喵神提醒,Optional其實是個enum,裏面有NoneSome兩種類型。其實所謂的nil就是Optional.None, 非nil就是Optional.Some, 然後會通過Some(T)包裝(wrap)原始值,這也是爲什麼在使用Optional的時候要拆包(從enum裏取出來原始值)的原因, 也是PlayGround會把Optional值顯示爲類似{Some "hello world"}的原因,這裏是enum Optional的定義:

enum Optional<</span>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<</span>U>(f: (T) -> U) -> U?
    func getMirror() -> Mirror
}


聲明爲Optional只需要在類型後面緊跟一個?即可。如:

var strValue: String?   //?相當於下面這種寫法的語法糖
var strValue: Optional<</span>String>  

上面這個Optional的聲明,意思不是”我聲明瞭一個Optional的String值”, 而是”我聲明瞭一個Optional類型值,它可能包含一個String值,也可能什麼都不包含”,也就是說實際上我們聲明的是Optional類型,而不是聲明瞭一個String類型,這一點需要銘記在心。

建議再讀一遍上段文字。

一旦聲明爲Optional的,如果不顯式的賦值就會有個默認值nil。判斷一個Optional的值是否有值,可以用if來判斷:

if strValue {
    //do sth with strValue
}

然後怎麼使用Optional值呢?文檔中也有提到說,在使用Optional值的時候需要在具體的操作,比如調用方法、屬性、下標索引等前面需要加上一個?,如果是nil值,也就是Optional.None,會跳過後面的操作不執行,如果有值,就是Optional.Some,可能就會拆包(unwrap),然後對拆包後的值執行後面的操作,來保證執行這個操作的安全性,比如:

let hashValue = strValue?.hashValue 

strValue是Optional的字符串,如果strValue是nil,則hashValue也爲nil,如果strValue不爲nil,hashValue就是strValue字符串的哈希值(其實也是用Optional wrap後的值)

另外,?還可以用在安全地調用protocol類型方法上,比如:

    

@objc protocol Downloadable {
    @optional func download(toPath: String) -> Bool;
}

@objc class Content: Downloadable {
    //download method not be implemented
}

var delegate: Downloadable = Downloadable()
delegate.download?("some path")


因爲上面的delegate是Downloadable類型的,它的download方法是optional,所以它的具體實現有沒有download方法是不確定的。Swift提供了一種在參數括號前加上一個?的方式來安全地調用protocol的optional方法。

另外如果你需要像下面這樣向下轉型(Downcast),可能會用到 as?

if let dataSource = object as? UITableViewDataSource {
    let rowsInFirstSection  = dataSource.tableView(tableView, numberOfRowsInSection: 0)
}


到這裏我們看到了?的幾種使用場景:

1.聲明Optional值變量

2.用在對Optional值操作中,用來判斷是否能響應後面的操作

3.用於安全調用protocol的optional方法

4.使用 as? 向下轉型(Downcast)

另外,對於Optional值,不能直接進行操作,否則會報錯:

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

let hashValue = strValue.hashValue

上面提到Optional值需要拆包(unwrap)後才能得到原來值,然後才能對其操作,那怎麼來拆包呢?拆包提到了幾種方法,一種是Optional Binding, 比如:

if let str = strValue {
    let hashValue = str.hashValue
}if let str = strValue {
    let hashValue = str.hashValue
}


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

直接上例子,strValue是Optional的String:

let hashValue = strValue!.hashValue 


這裏的!表示“我確定這裏的的strValue一定是非nil的,盡情調用吧” ,比如這種情況:

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操作時,每次依然要加上!來強制拆包(在讀取值的時候,也可以用?,謝謝iPresent在回覆中提醒),比如:

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

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

var myLabel: UILabel!  //!相當於下面這種寫法的語法糖
var myLabel: ImplicitlyUnwrappedOptional<</span>UILabel>


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

1.強制對Optional值進行拆包(unwrap)

2.聲明Implicitly Unwrapped Optionals值,一般用於類中的屬性

Swift是門新生的語言,我們有幸見證了它的誕生,激動之餘也在佩服蘋果大刀闊斧的推出一個新的語言替代一個已經比較成熟語言的魄力,今天在知乎日報上看到一個回答是說Swift是一門玩具語言,正當想去吐槽,發現回答已經被刪除了。個人認爲蘋果是很認真的推出Swift的,從Swift的各種細微的設計也能看的出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章