阿... 終於鐵人過半了,說快不快(說慢也不慢就是了)
Keep Going. 鐵人結束依然要繼續加油
Class 和 Struct 是一種多功能且靈活的構造體,作為程式碼中的構造基礎。你可以使用與常數,變數和函數完全相同的語法來定義屬性和方法,以便為 Class 和 Struct 增加功能。
與其他編程語言不同,Swift 不會要求你為自定義的 Class 和 Struct 單獨的端口實現文件,你可以在文件中定義一個 Class 或是 Struct ,並且這個 Class 或者 Struct 的外部端口被自動提供給其他程式碼使用。
一個 Class 的實例傳統上被稱為 "對象" 。然而,Swift 中的 Class 和 Struct 在功能上比其他語言更加接近。
Swift 中的 Class 與 Struct 有很多相同的地方,例如他們兩者都能:
Class 有,但 Struct 卻沒有的功能:
Struct 在程式碼中是透過複製來傳遞,並不會使用引數計數
Class 與 Struct 的定義語法的差別,只有差別在關鍵字的使用不同:
class ClassName {
}
struct StructName {
}
這邊我們創建一個叫 Cube 的 Class 和一個 Color 的 Struct
struct Color {
var red = 0
var green = 0
var blue = 0
var black = false
}
// 儲存三原色,紅、綠、藍的值以及黑色預設值是 false:
class Cube {
var length = 0
var witdh = 0
var height = 0
var color = Color()
}
// 儲存立方體的長、寬、高的變數,以及一個 color ,我們用 Color 結構體實例來初始化,它使屬性的類型被推斷為 Color
Color 的結構體定義和 Cube 的類定義只描述了「 什麼是 Color 和 Cube 」。因此,你需要創建一個 Struct 或 Class 的實例。兩者創建實例的方式也是大同小異:
let colorSet = Color() //創建 Struct 的實例
let cubeSet = Cube() //創建 Class 的實例
你可以使用點語法來拜訪 Class 或是 Struct 中的屬性,也就是只要在他們兩者的實例名稱後加上一個點(.),就能拜訪他們其中的屬性,例如我們要拜訪 colorSet 中的red:
你也可以拜訪子屬性,例如我們 cubeSet 下的 color 屬性下的 black 屬性:
所有的結構體都有一個自動生成的成員初始化器,你可以使用它來初始化新結構體實例的成員屬性。新實例屬性的初始化值可以通過屬性名稱傳遞到成員初始化器中:
let rgb = Color(red: 255, green: 0, blue: 0, black: false)
與 Struct 不同,Class 的實例不會接收默認的成員初始化器
值類型是一種當它被賦值到常數或者變數,或者被傳遞給函數時會被複製的類型。實際上,Swift 中所有的基本類型Int,Float-Point,Bool,String,Array 和 Dictionary 都是值類型,並且都以結構體的形式在後台實現。
所有結構和枚舉都是 Swift 中的值類型。 這意味著您創建的任何 Struct 和 enum 實例(以及它們作為屬性的任何值類型)在你的程式碼中傳遞時總是被複製。
這此我們宣告一個 redColor 的常數,並將設置為(red: 255, green: 0, blue: 0, black: false)的 Color 實例,之後並賦值給名為 apple 的變數,以當前 redColor 的值初始化:
let redColor = Color(red: 255, green: 0, blue: 0, black: false)
var apple = redColor
因為 Color 是一個結構體,現有實例的拷貝會被製作出來,然後這份新的拷貝就賦值給了 apple 。儘管 redColor 和 apple 有相同的屬性值,但是在後台中他們是兩個完全不同的實例。
如果我們這時將變數 apple 中的 red 進行修改,修改為 240:
apple.red = 240
我們在分別印出 apple.red 以及 redColor 中的值,你會發現 redColor 中的值依然還是 255:
簡單來說,apple 只是被賦了一個 redColor 當前值,也就是從 redColor 複製一份到 apple 中,所以他們只是值相同,但卻是兩個不同的實例,所以你修改 apple.red 值,redColor.red 當然也不會被改動,因為他們是不同的實例。
在枚舉(enum)中也是相同的意思,我們先創建一個方向的枚舉:
enum direction {
case up,down,left,right
}
並且我們也跟上面例子進行相同動作:
var goWhere = direction.down // 宣告一個名為 goWhere 的變數,值為 direction 枚舉中的 down
let goDown = goWhere // 宣告一個名為 goDown 的常數,複製一份 goWhere 的當前值給 goDown 作為值
goWhere = .up // 將 goWhere 中的值改為 direction.up
我們使用一個 if 判斷是來判斷 goDown 是不是不等於 .down :
也證明了兩者為不同實例,goDown 只是接收到一份從 goWhere 複製的當前值作為自己的值,所以修改 goWhere中的值不會影響到 goDown,goDown 值依然還是 down。
不同於值類型,引用類型在賦值給常數或變數或是傳遞給函數時不會被複製。相較於複製,這裡是對於同一個現有的實例進行引用。
首先我們先定義一個 square 常數,設置他引用一個 Cube 類的新實例。
let square = Cube()
square.color = redColor
square.height = 100
square.length = 100
square.witdh = 100
如果我們這時如同上面的 struct 和 enum 中,一樣將 square 宣告給另一個常數(ectangle),並將 ectangle 中的 heighnt 改為值 200:
let ectangle = square
ectangle.height = 200
這時我們來查看我們 square.height 中的值是否改變:
當時的我看到這邊也是滿頭的黑人問號,想說不是一樣存在於不同實例上,結果值也變了,何況他還是存在於一個常數中。雖然 square 和 ectangle 被宣告為一個常數,但你仍然可以更改 square.height和 ectangle.height,因為他們兩個常數本身的值並沒有改變。square 和 ectangle 本身並沒有儲存 Cube 實例,他們兩者都是在後台引用 Cube 實例。它是我們 Cube 中的屬性 height 在改變而不是引用 Cube 的常數的值在改變。
因為 Class 是引用類型,所以 square 和 ectangle 其實都是引用了相同的 Cube 實例。所以它們只是相同實例的兩個不同命名罷了。
由於類是引用類型,因此在後台可能有很多個常量和變量都是引用同一個類的實例,相同這詞在 struct 和 enum 就並非是真的相同,因為他們賦值給常數或變數時,或者傳遞給一個函數時他們總是被複製過去的。
有時候找出兩個常數或者變數是否引用來自於同一個 Class 實例是有用的,所以,Swift提供了兩個特徵運算符:
利用這兩個運算符來檢查兩者是否引用相同的實例:
這邊的相同於( === )意味著兩個類類型常數或者變量引用來自於相同的實例,而不是兩者相等(==)的意思。
您可以使用 Class 和 Struct 來定義自定義的數據類型,以用作程序代碼的構建塊。總而言之,Class 的實例是透過引用來傳遞的;Struct 則是透過複製來傳遞的,這也表示他的適用於不同類型的任務,所以當你考慮項目所需的數據結構和功能時,請確定每個數據結構是定義為 Class 還是 Struct。
按照通用準則,當符合以下一項或多項情況時可以考慮創建一個 Struct :
適合使用 Struct 創建的例子如下:
在其他情況下,你應該定義一個 Class 並創建該 Class 的實例,透過引用來管理和傳遞。實際上,這也代表著大多數自定義的數據結構應該為 Class 而不是 Struct。