Swift專題講解十五——類型構造

Swift專題講解十五——類型構造

1、引言

        構造是類、結構體、枚舉在實例化中必須執行的過程,在構造過程當中,類、結構體必須完成其中存儲屬性的構造。Swift中的構造經過構造方法來完成,和Objective-C中的init初始化系列方法不一樣,Swift中的構造方法並不會也無需返回值,它的任務便是完成實例化過程。javascript

2、屬性的構造

        類和結構體的存儲屬性必須在實例化完成前被構造完成,所以,有兩種方式來這麼作:java

1.類或者結構體中聲明存儲屬性時直接爲其設置默認值。安全

2.在類或者結構體的構造方法中對存儲屬性進行構造。閉包

這裏有一點須要注意:在存儲屬性設置默認值或者在構造方法中進行構造時,並不會觸發屬性監聽器willSet、didSet方法。示例代碼以下:ide

class MyClass {
    var count:Int=0{
        willSet{
            print("willset")
        }
    }
    var name:String
    init(){
        //必須進行構造或者設置值
        count=5
        count=6
        name = "HS"
    }
}

init()方法爲不帶參數的構造方法,全部構造方法都須要用init()來標識,開發者可使用函數重載的方式來建立不一樣的構造方法。官方推薦,若是一個類的大多實例的某個存儲屬性都須要相同的值,強烈推薦開發者設置此存儲屬性的默認值,這樣能夠很好的應用Swift語言的類型推斷功能而且可使代碼結構更加緊湊。函數

        若是一個屬性在邏輯上是容許爲nil的,則開發者能夠將其聲明稱Optional值類型,在進行類的實例化時,Optional類型的屬性若是沒有賦值會被自動賦值爲nil。ui

        注意,常量也須要在構造完成以前進行賦值,一旦賦值或構造完成,常量將不能被修改。spa

3、構造方法

        首先,若是類或者結構體中的全部存儲屬性都有默認值,那個若是開發者不提供構造方法,Swift也會自動生成一個默認構造方法,無參的init(),在進行類型的實例化時,將默認構造全部存儲屬性都是默認值的實例。示例以下:.net

class MyClassTwo {
    var count = 10
    var name = "HS"
}
//默認的init()構造方法
var obj = MyClassTwo()
print(obj.count,obj.name)

結構體會比較特殊,就算沒有爲其存儲屬性設置初值,它也會自動生成構造方法,這個構造方法中會自帶全部沒有賦默認值的屬性名做爲參數,示例以下:設計

struct Shape {
    var center:(Int,Int)
    var name:String
}
var shape = Shape(center: (1,1), name: "circle")

還有一點須要注意,對於值類型(結構體,枚舉),若是開發者自定義了一個構造方法,則默認的構造方法將會失效,這樣設計是爲了安全性考慮,防止誤用到系統的默認構造方法。而且,對於值類型(結構體,枚舉)的構造方法,是支持嵌套調用的,示例以下:

struct Shape {
    var center:(Int,Int)
    var name:String
    init() {
        center = (0,0)
        name = "HS"
    }
    init(param:String){
        self.init()
    }
}
var shape = Shape(param: "")

4、類的Designated構造方法與Convenience構造方法

        在前面的一篇博客中,我曾經專門討論過Swift中的構造方法,博客地址以下,可供參考:

Swift中的構造方法解析:http://my.oschina.net/u/2340880/blog/660134

        Designated構造方法也被稱爲指定構造方法,它是類的核心構造方法,指定構造方法將完成類中全部須要構造或賦值過程。若是一個類繼承於另外一個類而來,則指定構造方法須要調用父類的構造方法來完成父類中屬性的初始。Convenience工做方法也被稱爲便利構造方法,其主要做爲輔助的構造方法存在,便利構造方法須要調用類中的指定構造方法來完成構造,從這一點看,實際上類是經過便利構造方法來實現相似值類型的構造方法的嵌套使用。指定構造方法不須要多餘關鍵字來修飾,其默認就是Designated類型的,便利構造方法須要使用convenience關鍵字類修飾,示例以下:

class MyClassTwo {
    var count = 10
    var name = "HS"
    init(name:String){
        self.name = name
    }
    convenience init(count:Int){
        self.init(name:"HS")
        self.count = count
    }
}
var obj = MyClassTwo(count:5)
print(obj.count,obj.name)

類的構造方法須要遵照下面3條規則:

1.指定構造方法必須調用其父類的指定構造方法。

2.便利構造方法必須調用同類中的其餘構造方法。

3.便利構造方法調用到最上層必須調用一個指定構造方法。

語言文檔中提供以下示例圖來結束指定構造方法和便利構造方法的關係:

5、構造方法的安全特性

        Swift是一種十分注重類型安全的語言,這種語言特性的優點在於類在實例化後,全部的屬性都是開發者明確可控的。Swift的編譯器在類的構造方法中會進行4中安全性檢查:

檢查1:指定構造器中必須完成全部存儲屬性的賦值後才能調用父類的指定構造方法,示例以下:

class MyClassThree: MyClassTwo {
    var param:Int
    init(){
        param = 100
        super.init(name: "HS")
    }
}

檢查2:子類若是要自定義父類中存儲屬性的值,必需要調用父類的構造方法以後設置,示例以下:

class MyClassThree: MyClassTwo {
    var param:Int
    init(){
        param = 100
        super.init(name: "HS")
        //重設繼承父類屬性的name值
        self.name = "New"
    }
}

檢查3:若是便利構造方法中須要從新設置某些屬性的值,必須在調用指定構造方法以後設置,不然會被覆蓋。

檢查4:在完成父類構造方法以前,不能使用self來引用屬性。

6、構造方法的繼承

        Swift和Objective-C有很大不一樣,其構造方法不會被子類無條件的繼承。Swift中類的構造方法的繼承遵照下面兩個原則:

1.若是子類沒有定義任何的指定構造方法,則子類會默認繼承父類全部的指定構造方法。

2.若是子類中提供了父類全部指定構造方法,不管是覆寫的仍是繼承的,則子類會默認繼承下來父類的便利構造方法。

上面兩個原則可能有些難以理解,第1個原則實際上也說明子類若是定義了本身的指定構造方法,或者覆寫了父類的某個指定構造方法,則子類再也不繼承父類全部的指定構造方法。第2個原則能夠這樣理解:由於全部便利構造方法最終都要調用到指定構造方法,因此只要子類中有提供這個便利構造方法須要調用的指定構造方法,這個便利構造方法就會被繼承。

        重寫父類的指定構造方法須要使用override關鍵字,可是,便利構造方法並不存在重寫的概念,由於其必須調用本類的其餘構造方法,所以不管子類中定義的便利構造方法與父類是否相同,都是子類獨立的便利構造方法。

7、可失敗構造方法

        在開發中還會遇到一種狀況,某些構造方法須要傳入一些參數,當參數不符合要求時,此構造過程可能會失敗,這時,開發者可使用可失敗的構造方法來進行類型的構造,例如在類中建立可失敗的構造方法示例示例以下:

class MyClassThree: MyClassTwo {
    var param:Int
    init(){
        param = 100
        super.init(name: "HS")
        //重設繼承父類屬性的name值
        self.name = "New"
    }
    init?(suc:Bool){
        guard(suc)else{
            return nil
        }
        param=1
        super.init(name: "1")
    }
}

8、必要構造方法

        若是某些構造方法是類與其子類都必須實現的,則可使用required關鍵字來將其修飾爲必要的構造方法,子類必須繼承或者覆寫父類的必要構造方法,示例以下:

class MyClassThree: MyClassTwo {
    var param:Int
    required init(){
        param = 100
        super.init(name: "HS")
        //重設繼承父類屬性的name值
        self.name = "New"
    }
    init?(suc:Bool){
        guard(suc)else{
            return nil
        }
        param=1
        super.init(name: "1")
    }
}

還有一點須要注意,若是某些屬性的值設置十分複雜,開發者可使用閉包的方式來爲屬性設置初始值,示例以下:

class MyClassThree: MyClassTwo {
    //注意 閉包的後面必須加(),不然會將param當成一個閉包屬性來處理
    var param:Int = {
        return 6*6+6
    }()
    required init(){
        param = 100
        super.init(name: "HS")
        //重設繼承父類屬性的name值
        self.name = "New"
    }
    init?(suc:Bool){
        guard(suc)else{
            return nil
        }
        param=1
        super.init(name: "1")
    }
}

9、析構方法

        當類實例將要被釋放時,系統會自動調用類的析構方法,析構方法deinit()沒有參數和返回值,而且只有類有析構方法,開發者能夠在其中進行一些資源的釋放操做,當var類型變量被賦值爲nil時,實例會被釋放。

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索