標籤:表示 否則 沒有 不能 構造器 交流 關係 ... rect
構造過程為了產生類、結構體、枚舉等的執行個體,而做的準備過程,叫做構造過程。 為了這個過程,我們一般會定義一個方法來完畢,這種方法叫做構造器。當然它的逆過程,叫做析構器,用於在執行個體被釋放前做一些清理工作以及一此自己定義化的處理。
為儲存型屬性設定初始值類和結構體在產生執行個體那一刻,必須為全部的屬性賦以特定的初始值。
要麼在定義儲存型屬性的時候直接給個初始值,否則就必須在構造器裡面指定一個初始值。上面說的這兩種情況,都不會觸發儲存型屬性的監聽者行為(property observer)。
struct MyClass { var number: Int init () { //這個就是構造器,它沒有傳回值 number = 1 //為儲存型屬性指定一個初始值 }}
或者struct MyClass { var number: Int = 1 //直接在儲存型屬性定義的時候。指定一個初始值}
還能夠自己定義構造器,為它傳入參數。然後對屬性做對應的初始化:struct MyClass { var number: Int init (num: Int) { //這個就是構造器,它沒有傳回值 number = num //為儲存型屬性指定一個初始值 }
init (num: Int, byTimes: Int) { //能夠定義多個構造器 number = num * byTimes }}
let myInstance1 = MyClass(num:1) //使用第一種構造器產生一個執行個體let myInstance2 = MyClass(num:10, byTimes:3) //使用另外一種構造器產生還有一個執行個體注意:這裡與之前的類方法稍有不同,swift會為每一個參數都提供一個外部使用名,而這個行為在類方法中。是從第二個參數開始才有的(筆記十一中有提到)。當然你能夠指定一個外部使用名,來替代swift的預設行為(“_”依舊能夠禁止swift提供外部使用名)。
可選類型屬性假設我們在定義可選類型屬性的時候沒有賦值。而且,在構造器中也沒有賦值的話,swift為我們將這個屬性的值設定為nil。
常量屬性在構造過程結束之前。常量屬性的值是能夠任意改動的(當然要用相同類型的值)。注意:子類是不能夠改動父類的常量屬性的。
預設構造器swift會為那些沒有構造器,而且全部屬性都有初值的類,提供一個預設構造器(編譯器做的事。在swift代碼中看不見),上面已經出現過這種一個類了:struct MyClass { var number: Int = 1}
這個類中,我們並沒有指定一個構造器,而且number屬性也有初始值。那麼swift就會提供一個預設的構造器,使得number屬性確實等於1......(對於新手來說,這可能非常難理解,就先這麼記吧,由於這塊涉及到記憶體配置的問題,暫且不展開記錄。後繼等把手冊全都看完,我還想寫個對swift中的各種類型變數做個記憶體模型的分析的系列筆記。到時候再來說這個問題。
)
結構體的屬性列表構造器(Memberwise Initializers for Structure Types)在結構體中,除了上面說過的預設構造器外,swift會提自己主動接受一個”全部用於儲存的屬性帶有初始值的列表“的構造器,前提是我們沒有為struct寫自己定義構造器。
struct MyStruct { var name = “1” var number = 2}
let myStructInstance = MyStruct(name: “Hello”, number:100)這相當於提供了一個 init(name:String, number:Int) 的構造器,但這個行為是swift幫我們做的。
實值型別的構造器代理構造器還能調用其他的構造器作為構造過程的一部分。這個過程,叫做構造器代理。
實值型別(結構體,枚舉)並不支援繼承,所以,他們的構造器代理與類的構造器代理的規則和形式有所差別。實值型別的構造器相對簡單,由於類會繼承非常多父類的儲存型屬性,類就有責任在構造過程中保證它繼承來的屬性被正確的初始化(這個等會再說)。
先來看一個官方的範例:
這個範例中,有三個構造器,第一個是個空的{}。當這個構造器被調用的時候:let myRect = Rect()origin會是 (0.0, 0.0), size會是 (0.0, 0.0),所有都是初始值。
第三個構造器中(第12行)調用了第二個構造器,這樣的方法,能夠降低第三個構造器的代碼。假設沒有這樣的方式的話,那麼在這個位置,我們就要把第二個構造器的內容複寫一份,放在這裡(這顯然是不好的)。
類繼承和構造過程switf為類提供了兩種構造器,以確保全部的屬性都能被值始化:指定構造器 和 便利構造器
//吐槽:原本為了方便交流而定義了各種各樣的名字,結果在上手學習的時候。卻造成了極大的障礙。不知道這算不算是得不嘗失。
但既然已經有人給各種各樣的調用方式都起了名字,而且又出如今官方文檔裡。那就逆來順受吧,僅僅要我們知道,事實上這玩意就是那麼回事。。。
即可了
這裡的指定構造器和便利構造器在官方文檔上說了一大堆的解釋,各種名字概念天花亂墜。說得頭都暈了。無非就是為了方便使用而封裝了一層調用而已啊,繞暈有什麼意思,還是臨時先放一下吧,一會兒再說。
構造器調用鏈為了簡化指定構造器和便利構造器的調用關係,swift指定了三條規則:1. 構造器代理必須調用它的直接父類的指定構造器2. 便利構造器必須調用它同一個類中定義的其他構造器3. 便利構造器的結尾部分必須調用一個指定構造器
一個方便的記憶方法是(這是文檔上的):指定構造器必須總是向上代理便利構造器必須總是橫向代理
//吐槽:說得好聽點是為了簡化而搞出來的一堆並不簡單的規則。說難聽點不就是為了商業目的,減少門檻而搞出來的所謂自己主動推導而引出來的一系列的噁心問題嘛。
。。。
向上代理的意思就是調用父類的指定構造器橫向代理的意思就是調用它自己類內的構造器官方的圖解:
每個大的藍色廣塊表示的是一個類。每個小的藍色廣塊(寫著 Designated)代表指定構造器,每個土黃色的方塊(寫著 Convenience)代表便利構造器。 這個圖就是對上面三條規則的解釋。
兩段式的構造過程(Two-Phase Initialization)swift中類,包括兩個階段。第一階段,每一個儲存型屬性通過它們自己的類構造器來設定初始值。第二階段。每個儲存型屬性都能夠進一步的設定他們定製化的初始化。
我拿一小段代碼來說明這個文檔的SB之處(這段代碼明顯是錯的):int myNumA = 1
func myFunc() { myNumA = myNumB //在這裡的時候 myNumB還沒定義,也沒初始化,明顯是錯的}
int myNumB = 2 //myNumB定義+初始化
所謂的兩段式構造過程,就是告訴我們。在使用myNumB之前。要確保它被定義而且被初始化了。
。。。。
於是。假設按著兩段式構造過程,我們上面的代碼正確寫法應該是:
int myNumA = 1int myNumB = 2
func myFunc() { myNumA = myNumB}//看起來是不是非常好笑。
。。
。用了一大堆的描寫敘述,就是想說明這種問題。
構造器的繼承和重寫swift中,子類不會預設的繼承父類的構造器。可是我們偶爾會認為父類的構造器的功能不夠強大,這個時候就須要在子類中,重寫構造器,以達到特定的目的。
假設重寫的是一個指定構造器,寫完我們的邏輯之後,記得要調用父類的那個構造器。假設重寫的是一個便例構造器,必需要調用這個類中的其他指定構造器。
構造器的重寫,與屬性、方法以及下標的重寫不同。我們不須要在前面寫 overridekeyword。
自己主動構造器的繼承在之前的筆記中以及文檔中,一直在說。swift不會預設的繼承父類的構造器,但實際上,非常多範例已經說明能夠自己主動繼承了(為了避免誤解與暴亂,扔白菜之類的事情發生,我一直都沒說。。。:))
swift的構造器繼承機制,要滿足下面兩點:1. 假設子類未定義不論什麼指定構造器,它將會自己主動繼承全部父類的指定構造器。2. 假設子類中定義了全部父類的指定構造器的實現(通過規則1 或是 自己實現),它將會自己主動繼承全部父類的例利構造器。
指定構造器及便利構造器的文法指定構造器:init (參數) { 函數體}
便利構造器:convenience init(參數) { //這裡有個conveniencekeyword 函數體}
僅僅要知道convenience型的構造器,僅僅能調用它自己這個類中的非convenience構造器即可了。
用閉包或者函數設定屬性的預設值class MyClass { var myNumbers:Int[] = { var tmpNum = Int[]() //產生一個暫時數組。用來在閉包最後給myNumbers賦值 for i in 1…10 { tmpNum.append(i) //加入元素 } return tmpNum //閉包的傳回值 }() //這個()一定不能夠省,不然的話,myNumbers就成了閉包這個函數,而不是閉包的傳回值了
func getNumbers() -> Int[] { return myNumbers }}
let myInstance = MyClass()myInstance.getNumbers() //這條取到了MyClass的myNumbers數組
swift 筆記 (十四) —— 構造過程