swift中,咱們常常看到和用到?和!,今天來聊一聊它們倆。編程
swift編程,不外乎是定義屬性或者函數(方法),訪問屬性或者調用函數,類型轉換,?和!在這幾個過程當中,都有一展身手的時候,並且,每次要考慮使用的時候,它們倆都會一塊兒出如今咱們的大腦中,用仍是不用,若是用,要用誰?swift
一、定義屬性安全
「?」表示可選類型(Optionals),「!」表示隱式可選類型(全名:implicitly unwrapped optionals),其實仍是可選類型。app
可選類型,就是將已存在的某種類型(結構體、枚舉、類)定義爲可選類型,表示該「新」類型的值能夠爲空nil。書寫格式就是在原來的類型後面跟一個「?」好比:函數
var nickName : String?指針
定義了一個可選類型String?的變量nickName,若是咱們不在構造函數(init這類函數)中給nickName賦初值,那麼,系統會默認給它一個nil爲初值,其實,咱們定義的可選類型的時候,系統立刻就把可選類型變量或常量初始化爲nil了,在調用init方法以前。code
在訪問可選類型屬性時,若是咱們肯定該屬性在這個時候必定有值,能夠在屬性名稱後面加「!」,告訴系統,「我確定這個可選屬性有值,強制取出來用」,可是,若是這個可選類型屬性當時的值爲nil,那麼會crash。呵呵。(若是訪問屬性或者調用函數,必須用「!」或者「?」,下文再說),照樣訪問,這樣,若是是nil,也不會由於這個致使crash。因此,通常在程序的關鍵點使用可選類型以前,都會作安全檢查,判斷是否爲實際值是否爲nil。判斷方法有兩種:繼承
swift:生命週期
if nickName != nil { print("\(nickName)") }ci
if let tempName = nickName { print("\(tempName)") }
//第二種寫法叫可選綁定,意思是,若是可選類型nickName不爲nil,就取出其中的值,賦給tempName,而且類型推斷tempName爲NSString,由於肯定等號後面的那個量不爲nil才執行賦值,因此,tempName 不是可選類型,賦值成功,此時至關於if true {。。。},若是nickName此時的值爲nil時的狀況,請自行腦補。
「 !」 ,隱式可選類型,就是可選類型,書寫位置同 「 ?」,區別是,用「!」聲明的可選類型,訪問屬性的時候均可以不用寫「!」來取出屬性的真實值,這就告訴系統,我知道這個屬性在其存活過程當中一直會有值。其實,在控制器中訪問本身的可選類型屬性是須要用「!」來取值的,這個動做官方叫強制解包(forced unwrapping)。上面說取值,爲了方便理解。還能專業點麼?下統一用解包。
var nickName :String!
注意:若是隱式可選類型屬性在生命週期中可能會爲nil,那仍是不要將其定義爲「!」類型的了。會crash的。
二、定義函數?(你在逗我?)
「?」和「!」是能夠用來定義函數的。類、結構體、枚舉都是須要構造器的,那麼,有時候根據某種業務要求,咱們會斷定,本次實例構造過程失敗,因而,就有了可失敗構造器。官方文檔裏面舉例以下:
class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
因此,看到init?和init!的時候不要慌,就是要實例化的類型多是空,可能構造(建立)失敗,失敗的時候會返回nil,雖然swift不想OC中那樣須要在init方法中返回self。上例中,若是構形成功,好比:
let newProduct = Product("巧克力")
根據Product類定義,構造實例成功,newProduct的類型推斷爲Product?。(除非你硬要在接收實例的變量或常量後面加個可選類型,不要做,真的。。。)
做爲基友,怎麼能沒有「!」的事!其實做用同樣,只是,若是構形成功,返回實例的類型推斷爲Product!(隱式可選)
有一點須要強調的是,若是當前實例化的類爲某個類的子子子類,好比咱們自定義了一個UIButton的子類BaseButton,又定義了PYButton :BaseButton,實例化PYButton的時候,若是失敗了,那麼在實例化過程當中繼承者鏈條不管進行到哪一步了,就立刻中止。
三、訪問屬性和調用函數
這倆一塊兒聊了,在咱們訪問屬性或者調用函數的時候,由於swift容許各類連語法連在一塊兒用,因此,有時候會出現一大串點語法連續調用,屬性訪問和函數調用相互交叉,在這個過程當中,若是遇到可選類型怎麼辦?要寫一堆「!」解包?答案是否認的。
swift提供了一個機制,叫「可空鏈式調用」,在一連串的點語法調用和訪問中,只須要在調用者後面加一個「?」,表示,若是這一串點語法執行過程當中,任何一步失敗,整個點語法鏈條就會中止,而且做爲一個總體返回nil,不會報錯,不會crash。那麼,問題來了,用「!」不行麼?能夠!可是,若是在點語法鏈條中,那一步返回值爲nil,就會致使crash。既然咱們無法永遠保證某塊代碼返回值不爲nil,或者屬性爲nil,或者失敗,那麼用「 ?」是極好的。
上代碼:
class Person {
var telString :String?
}
let xiaoMing = Person()
print("\(xiaoMing.telString!.intValue)") //crash,由於telString爲nil,強制解包就crash
print("\(xiaoMing.telString?.intValue)") //輸出:nil
四、類型轉換
你們都知道OC中有多態,某些狀況下,咱們會將子類實例賦值給父類指針,用到的時候,再強轉回子類。swift中,這種狀況,使用as? 和 as! 來作,若是要判斷某個實例的類型 用 is。 A as? B的意思是,若是實例A是B類型或者是B類型的子類,就將A的類型轉化成B類型,並返回轉換後的實例(是可選類型B?),若是不是,表達式返回nil,程序繼續運行。若是用as! ,說明咱們確定A是類型B或者B的子類實例,那麼,強制轉換,若是不是,那麼會crash。好比,
class Teacher : Person {
var teachingID : String
init(teachingID : String) {
self.teachingID = teachingID
super.init()
}
}
class Student : Person {
var studentID : String
init(studentID : String) {
self.studentID = studentID
super.init()
}
}
let teacher = Teacher("12345")
let student = Student("987")
let classContact = [teacher,student]
//注意:此時classContact的類型是:[Person]
for person in classContact {
if let teacher = person as? Teacher { //code}
else if let student = person as? Student { // code}
}
as!的狀況自行腦補。簡單提一下,is,它就至關於 OC中的 isKindOfClass:方法。
另外,還有兩個問號連在一塊兒用的,其實也很簡單,先看代碼:
let name = 「panyu」
let nickName = name ?? "hehe" //若是name爲nil(固然本例不會爲nil),那麼表達式返回值爲"??"以後的值,若是name有值,那麼表達式返回name自己的值
一句話,?? 表達式就是三目運算符的簡單版。