iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 24
0
Software Development

給我 30 天,給你一輩子:Swift 從零開始系列 第 24

Day 24 | Swift Reference Type 與 Value Type

  • 分享至 

  • xImage
  •  

Reference Type 與 Value Type

在前幾篇介紹 Struct 與 Class 時,有提到一個是 Value Type,另一個則是 Reference Type,今天再仔細探究一下這兩者之間的差異以及使用情境。

Class 就是一個經典的 Reference Type,除此之外還有 Functions 以及 Closure 都是 Reference Type。

至於 Value Type,以之前舉的例子,Struct 就是 Value Type,像是 String、Array、Dictionary 都是以 Struct 來實作,所以都是 Value Type。在 Swift Standard Library 中可以發現,大量使用了 Value Type。

Reference Type

class Car {
    var wheelNumber = 4
}

let car1 = Car()
let car2 = car1

car1.wheelNumber = 8
print(car2.wheelNumber) // 8

上面這個類別代表著這個車會有四個輪胎,假定我們透過實體化建立 car1 實體,這時候就會指向一個記憶體位址來給 car1 參考,此時我們在建立一個 car2 並且指派 car1 給它,這時候 car2 會指向與 car1 同一個記憶體位址。

所以他們兩個現在共享同一個記憶體位址,也因此當我們透過其中一個實體,來更改屬性 wheelNumber,另外一個實體在呼叫時也會跟著更改。

Value Type

至於 Value Type 就是另外一回事了,跟 Reference Type 完全不同,我們先來看一個簡單的範例:

var value1 = 100
var value2 = value1
value2 += 100

print(value1) // 100
print(value2) // 200

value2 變數的值是參考 value1,當 value2 改變數值時,value1 不會受到影響,因為這兩個變數的記憶體空間指向不同位址,如果把上面的 Car 類別改成 Struct 看看:

struct Car {
    var wheelNumber = 4
}

var car1 = Car()
var car2 = car1

car1.wheelNumber = 8
print(car2.wheelNumber) // 4

Mutability

在 Reference Type 中,儘管在宣告實體時,用的是 let 來宣告為常數,還是可以透過實體來修改屬性值,但是在 Value Type 如果這樣做,是更改不了屬性值的,為什麼會這樣?

  • 對於 Reference Type,使用 let 宣告的實體,不能再被指派其他資料,但是可以更改實體本身的屬性或是方法。
  • 對於 Value Type,使用 let 宣告的實體,不僅不能再被指派其他資料,連同實體本身的屬性也都會被視為常數而不能被更改,儘管屬性宣告時是使用 var

使用時機

對於 Value Type 以及 Reference Type 該如何來取捨及使用呢?

可以使用 Value Type 的情境:

  • 當資料可以被合理使用 == 來比較值的時候
struct People {
    var name: String
}

let people1 = People(name: "安竹")
let people2 = People(name: "安竹")

在這種情況下,我們可以說 people1people2 相等,我們可以合理使用 == ( 需要遵循 Equatable Protocol ) 來判斷兩者資料是否相符。

  • 當希望實體與實體間可以有獨立的狀態
struct People {
    var name: String
}

var people1 = People(name: "安竹")
var people2 = People(name: "安竹")

people1.name = "皮皮"
print(people2.name) // 安竹

當 people1 已經更改名稱時,並不會影響到 people2。

  • 當資料被多個執行緒呼叫使用時,避免造成資料同時間更改而造成混亂

可以使用 Reference Type 的情境:

  • 當資料可以被合理使用 === 來比較值的時候
class People {
    var name: String = "安竹皮皮"
}

var people1 = People()
var people2 = people1

print(people2 === people1) // true

可以透過 === 來檢查兩者記憶體儲存位址是否相同,進而判斷兩個物件相同與否。

  • 想建立一個可以被共享或是同時更動的資料
class 午餐 {
    var 員工配額 = 240
}

class 員工 {
    var 午餐費用: 午餐
    
    init(午餐費用: 午餐) {
        self.午餐費用 = 午餐費用
    }
}

let 午餐費用 = 午餐()
let 員工甲 = 員工(午餐費用: 午餐費用)
let 員工乙 = 員工(午餐費用: 午餐費用)

print(員工甲.午餐費用.員工配額) // 240
print(員工乙.午餐費用.員工配額) // 240

午餐作為一個 class,並且再宣告一個 class 員工,每個員工都有一筆午餐費用,這是參考 午餐 class,每人的員工午餐配額都是一致的,所以這樣就不會有人配額比較高,而造成內心小劇場。
/images/emoticon/emoticon04.gif


上一篇
Day 23 | Swift Property 一家親:Type Property 和 Property Observer
下一篇
Day 25 | Swift Subscripts
系列文
給我 30 天,給你一輩子:Swift 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言