swift之?和!的含義(轉)

來源:互聯網
上載者:User

標籤: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之?和!的含義(轉)

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.