swift中問號?和嘆號!

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

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

Optional其實是個enum
,里面有NoneSome兩種類型。其實所謂的nil就是Optional.None
,非nil就是Optional.Some, 然后會通過Some(T)包裝(wrap)原始值,這也是為什么在使用Optional的時候要拆包(從enum里取出來原始值)的原因, 也是PlayGround會把Optional值顯示為類似{Some "hello world"}的原因,這里是enum Optional的定義:

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只需要在類型后面緊跟一個?
即可。如:

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

上面這個Optional的聲明,意思不是”我聲明了一個Optional的String值”, 而是”我聲明了一個Optional類型值,它可能包含一個String值,也可能什么都不包含”,也就是說實際上聲明的是Optional類型,而不是聲明了一個String類。

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

if strValue 
{ 
//do sth with strValue
}

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

let hashValue = strValue?.hashValue

strValue是Optional的字符串,如果strValuenil,則hashValue也為nil,如果strValue不為nil,hashValue就是strValue字符串的哈希值(其實也是用Optional wrap后的值)另外,?還可以用在安全地調(diào)用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,所以它的具體實現(xiàn)有沒有download
方法是不確定的。Swift提供了一種在參數(shù)括號前加上一個?
的方式來安全地調(diào)用protocol的optional方法。
另外如果你需要像下面這樣向下轉(zhuǎn)型(Downcast),可能會用到as?:

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

?的幾種使用場景:

  • 1.聲明Optional值變量。
  • 2.用在對Optional值操作中,用來判斷是否能響應后面的操作。
  • 3.用于安全調(diào)用protocoloptional方法。
  • 4.使用 as? 向下轉(zhuǎn)型(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
}

一種是在具體的操作前添加!符號,比如
strValue是Optional的String:

let hashValue = strValue!.hashValue

這里的!表示“我確定這里的的strValue一定是非nil的,隨便使用” ,比如這種情況:

if strValue { 
let hashValue = strValue!.hashValue
}

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

假定有一個自定義的MyViewController類,類中有一個屬性是myLabel,myLabel是在viewDidLoad中進行初始化。所以不能直接聲明為普通值:var myLabel : UILabel,因為非Optional的變量必須在聲明時或者構(gòu)造器中進行初始化,但就是想在viewDidLoad中初始化,就只能聲明為Optional:var myLabel: UILabel?, 雖然確定在viewDidLoad中會初始化,并且在ViewController的生命周期內(nèi)不會置為·nil·,但是在對myLabel操作時,每次依然要加上!來強制拆包(在讀取值的時候,也可以用?,比如:

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

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

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

所以!大概也有兩種使用場景

  • 1.強制對Optional值進行拆包(unwrap)
  • 2.聲明Implicitly Unwrapped Optionals值,一般用于類中的屬性
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容