標籤:ash 統一 多態 構造 程式 停止 執行個體化 一個 枚舉
swift中,我們經常看到和用到?和!,今天來聊一聊它們倆。
swift編程,不外乎是定義屬性或者函數(方法),訪問屬性或者調用函數,類型轉換,?和!在這幾個過程中,都有一展身手的時候,而且,每次要考慮使用的時候,它們倆都會一起出現在我們的大腦中,用還是不用,如果用,要用誰?
1、定義屬性
“?”表示可選類型(Optionals),“!”表示隱式可選類型(全名:implicitly unwrapped optionals),其實還是可選類型。
可選類型,就是將已存在的某種類型(結構體、枚舉、類)定義為可選類型,表示該“新”類型的值可以為空白nil。書寫格式就是在原來的類型後面跟一個“?”比如:
var nickName : String?
定義了一個可選類型String?的變數nickName,如果我們不在建構函式(init這類函數)中給nickName賦初值,那麼,系統會預設給它一個nil為初值,其實,我們定義的可選類型的時候,系統馬上就把可選類型變數或常量初始化為nil了,在調用init方法之前。
在訪問可選類型屬性時,如果我們確定該屬性在這個時候一定有值,可以在屬性名稱後面加“!”,告訴系統,“我肯定這個可選屬性有值,強製取出來用”,但是,如果這個可選類型屬性當時的值為nil,那麼會crash。呵呵。(如果訪問屬性或者調用函數,必須用“!”或者“?”,下文再說),照樣訪問,這樣,如果是nil,也不會因為這個導致crash。所以,一般在程式的關鍵點使用可選類型之前,都會做安全檢查,判斷是否為實際值是否為nil。判斷方法有兩種:
swift:
if nickName != nil { print("\(nickName)") }
if let tempName = nickName { print("\(tempName)") }
//第二種寫法叫可選綁定,意思是,如果可選類型nickName不為nil,就取出其中的值,賦給tempName,並且類型推斷tempName為NSString,因為確定等號後面的那個量不為nil才執行賦值,所以,tempName 不是可選類型,賦值成功,此時相當於if true {。。。},如果nickName此時的值為nil時的情況,請自行腦補。
“ !” ,隱式可選類型,就是可選類型,書寫位置同 “ ?”,區別是,用“!”聲明的可選類型,訪問屬性的時候都可以不用寫“!”來取出屬性的真實值,這就告訴系統,我知道這個屬性在其存活過程中一直會有值。其實,在控制器中訪問自己的可選類型屬性是需要用“!”來取值的,這個動作官方叫強制解包(forced unwrapping)。上面說取值,為了方便理解。還能專業點嗎?下統一用解包。
var nickName :String!
注意:如果隱式可選類型屬性在生命週期中可能會為nil,那還是不要將其定義為“!”類型的了。會crash的。
2、定義函數?(你在逗我?)
“?”和“!”是可以用來定義函數的。類、結構體、枚舉都是需要構造器的,那麼,有時候根據某種業務要求,我們會判定,本次執行個體構造過程失敗,於是,就有了可失敗構造器。官方文檔裡面舉例如下:
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的時候,如果失敗了,那麼在執行個體化過程中繼承者鏈條無論進行到哪一步了,就馬上停止。
3、訪問屬性和調用函數
這倆一起聊了,在我們訪問屬性或者調用函數的時候,因為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
4、類型轉換
大家都知道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本身的值
一句話,?? 運算式就是三目運算子的簡單版。
swift之?和!的含義(轉)