iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0
Mobile Development

Swift30天:從語法到觀念,告訴你在踏入實作前最好弄清楚的那些事系列 第 10

# Day10--只有藍圖是不夠的!我們要來變出一個實體!

  • 分享至 

  • xImage
  •  

爲什麼需要初始化?

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that’s required before the new instance is ready for use.

初始化是一種過程,準備類別、結構或枚舉的使用。這個過程涉及設定一個初始化值給儲存屬性以及實體,和展示任何其他的設定或初始化,這是在生成一個實體前必要存在的事情。

在官方網站上大致上是這樣去詮釋初始化這個詞的,不過,有一個更精確的說法是:「類和結構必須在創建該類或結構的實例時將其所有存儲的屬性設置為適當的初始值。存儲的屬性不能處於不確定狀態。

也就是說,其實初始化這件事情的本身,就是為了讓「儲存屬性」的狀態被確定。

我們確定了為什麼要初始化之後,接著要討論的事情也就圍繞在初始化器上了。

初始化器最基本的樣子:

init(){
   //裡面是要初始化的內容
}
  1. 默認初始化器(Default Intializer)
struct SomeStruct{
    var someText = "Text"
}

如上面所述,這樣就是已經內含的默認初始化器,因為我們直接把值指派給了一個字串。

而,初始化這件事情除了默認之外,其實也可以是客製的,下面這邊會稍加描述:

  1. 客製初始化器(Customizing Intailizer)
struct SomePerson{
    var name:String
    var height:Int
    var weight:Int
    init(name:String ,height:Int ,weight:Int){
        self.name = name
        self.height = height
        self.weight = weight
    }
}

上面的初始化器中,就很明顯的定義了在這個結構中,若你要使用這個結構實體化後的結果,就必須滿足在它的參數中所要求的項目。

而初始化器其實也跟函式相似,可以提供它內部參數與外部參數:

struct BMIStruct{
    var BMI:Int
    init(fromHeight height:Int, fromWeight weight:Int){
    BMI = weight / (height/100)*(height/100)
    }
    init(fromKenHeight KenHeight:Int ,fromKenWeight KenWeight:Int){
    BMI = KenWeight / (KenHeight/100)*(KenHeight/100)
    }
}
let KenBMI = BMIStruct(fromKenHeight:170, fromKenWeight:60)
print(KenBMI.BMI)

然而,上述基本上是Struct的初始化器,Class的初始化其實在初始化器的樣子、規則其實跟Struct一樣,都是在賦予值與否的問題。

然而,Class跟Struct的初始化器其實擁有兩個很大不同之處在於:
class具有繼承(Inheritance),而Struct並沒有,所以這讓class的初始化更具有些許複雜性。

反初始化

引自《The Swift of Programming Language》中文版:

析構原理

在一個類別的實例被釋放之前,析構函式被立即呼叫。用關鍵字deinit來標示析構函式,類似於初始化函式用init來標示。析構函式只適用於類型別。
Swift 會自動釋放不再需要的實例以釋放資源。如自動引用計數那一章描述,Swift 通過自動引用計數(ARC)處理實例的內存管理。通常當你的實例被釋放時不需要手動地去清理。但是,當使用自己的資源時,你可能需要進行一些額外的清理。

100Days of Swift的文章亦指出:

The job of deinitializers is to tell us when a class instance was destroyed. For structs this is fairly simple: the struct is destroyed when whatever owns it no longer exists. So, if we create a struct inside a method and the methods ends, the struct is destroyed.

析構器的工作是告訴我們什麼時候一個類別實例會被破壞。對於結構來說,這非常的簡單:結構的廢棄是當我們的擁有者再存在,所以,如果我們創造了一個方法在結構裡面,而方法結束後,結構也隨之廢棄。

Behind the scenes Swift performs something called automatic reference counting, or ARC. ARC tracks how many copies of each class instance exists: every time you take a copy of a class instance Swift adds 1 to its reference count, and every time a copy is destroyed Swift subtracts 1 from its reference count. When the count reaches 0 it means no one refers to the class any more, and Swift will call its deinitializer and destroy the object.

在螢幕後,Swift呈現了某件事情:自動參考計數,或稱之為ARC,ARC的功能是在追蹤有多少個類別的實例存在,一旦你複製出一個實例,ARC就會自動的在參考計數上加一,以及每次一個複製廢棄後,Swift就會從實例計算中減去1。當計算達到零時,意指沒有對象可從類別中再找到東西了,Swift就會呼叫析構器去廢棄物件。

總而言之,析構在其目的上,是為了將不需要的實例資源釋放。而Swift有它自己的實例內存管理的方法,通常實例產生時,不會需要開發者動手去清理,但我們仍有可能碰到需要清理的時候,尤其是我們使用自己的資源,而常見者如:

如果創建了一個自定義的類別來打開一個文件,並寫入一些資料,你可能需要在類別實例被釋放之前關閉該文件。

上述之文字所及,也就是說,析構的使用,是基於類別實體被釋放前的運作機制,我們通常創建了一個類別後,底下會有建構器,但為了避免資料一直處於打開狀態,所以希望在建構器後,能將那些引入的資料關閉,這個時候,就需要析構器來扮演這樣的角色。

析構器

在類別的定義中,每個類別最多只能有一個析構函式。析構函式不帶任何參數,在寫法上不帶括號:

deinit {
    // 執行析構過程
}

析構函式是在實例釋放發生前一步被自動呼叫。

因此,析構器有一些特點:

不允許主動呼叫自己的析構函式。
子類別繼承了父類別的析構函式,並且在子類別析構函式實作的最後,父類別的析構函式被自動呼叫。

即使子類別沒有提供自己的析構函式,父類別的析構函式也總是被呼叫。

因為直到實例的析構函式被呼叫時,實例才會被釋放,所以析構函式可以存取所有請求實例的屬性,並且根據那些屬性可以修改它的行為(比如查找一個需要被關閉的文件的名稱)。
就以下列舉例來說:

其實析構器就是在類別結束的時候,作為是將整個結構的內存清空的一種存在。

總之,關於析構器這件事情,在100Days of Swift中Paul Hudson在文章的結尾處是這樣說的:

So, the simple reason for why structs don’t have deinitializers is because they don’t need them: each struct has its own copy of its data, so nothing special needs to happen when it is destroyed.

所以,結構不需要析構器的最簡單理由,是因為他們不需要它:每個結構都有自己擁有的複製的資料,所以當它們被銷毀的時候,沒有什麼特別需要注意的(這裡指結構為什麼沒有析構器)

You can put your deinitializer anywhere you want in your code, but I love this quote from Anne Cahalan: “Code should read like sentences, which makes me think my classes should read like chapters. So the deinitializer goes at the end, it’s the ~fin~ of the class!”

翻譯翻譯:你可以讓你的析構器隨便放置,但我比較傾心於Anne Cahalan所說的:程式碼應該讀起來像句子,這樣讓我覺得我的類別讀起來像是讀一個篇章。所以,析構器放置在最底,就像是:這個篇章的結尾~

tags: 鐵人賽

上一篇
# Day9--老爸,我可以繼承你的家產,但我不想長得太像你
下一篇
# Day11--枚舉:讓你「有秩序」的管理「有順序」的項目
系列文
Swift30天:從語法到觀念,告訴你在踏入實作前最好弄清楚的那些事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言