OOP: Reference Type And Value Type in Memory

LukeWu
Aiworks
Published in
7 min readMar 21, 2019

物件導向程式設計 (OOP, Objective-Oriented-Programming) 是軟體開發其中一個重要的概念,而了解 Reference Type 的特性則是接觸 OOP 的起手式,這是在學習程式開發的過程,其中一個新手比較難跨過越的門檻。

本文試著從記憶體的角度出發,比較 Reference Type 與 Value Type 在記憶體中行為上的差異,希望能夠幫助大家更了解 Reference Type 與 Value Type。

Memory

要談 Reference type 跟 Value type 之前,應該要先從了解實際的記憶體 (Random Access Memory,簡稱 RAM) 長什麼樣子開始:

記憶體(RAM)是一個一個的儲存空間,功能為儲存資料,每一個記憶體都有自己的記憶體位址 (Memory Address),電腦就可以透過這個 Memory Address 找到特定的記憶體,拿出裡面所儲存的資料;你可以把記憶體想像成是一個一個的貨櫃,可以儲存貨物,每一個貨櫃,都有特定的編號,方便尋找與紀錄。

而現實世界中的各種資料,在電腦的世界都會被轉化成 1010…0101 的形式儲存,也就是所謂的 Binary data ,需要的時候再透過既定的方法,將 Binary data 轉成現實世界中,人類看得懂的資料格式。

Value Type

以 iOS 所使用的 Swift 語言為例,當我們寫下這樣的程式碼,到底電腦做了哪些事:

= 這個符號,在 Swift 中稱為 Assignment Operator,它會將位於 Operator 右邊的資料,儲存到左邊的變數中。如果在右手邊的是一個運算式 (Expression),它則會將右邊運算式的結果,儲存到左邊的變數中。

而當電腦執行到 var price = 4,會做以下的事情:

1. 先在 RAM 中,找到一個閒置的 memory。

2. 將 price 這個變數,與我們剛剛找到的 memory 之 memory address 關聯起來。

3. 將 4 這個數字,轉成 Binary data,並儲存在這個 memory 中。

4. 下次我們再使用到 price 這個變數的時候,電腦就會知道我們指的是這個 memory address,繼而去找到這個 memory,將其儲存的資料取出來使用。

接著,var anotherPrice = price

1. 先在 RAM 中,找到一個閒置的 memory。

2. 將 anotherPrice 這個變數,與我們剛剛找到的 memory 之 address 關聯起來。

3. 找到變數 price 所對應到的 memory,將其儲存的資料複製一份,放到 anotherPrice 變數所對應到的 memory。

最後,anotherPrice = 10

1. 找到 anotherPrice 所對應的 memory。

2. 將 10 轉成 binary data 並取代原先的資料。

priceanotherPrice 擁有各自的資料,存放在各自相對應的記憶體內,兩變數並不是共用一份資料,因此執行完 anotherPrice = 10 之後,我們將兩個變數各自 print 出來,會發現 price 所儲存的內容並沒有被改變,這種資料的 獨立性,就是 Value Type 的特性。

Console 將會印出:

Price is 4.
AnotherPrice is 10.

Reference Type

接著,讓我們來看看 Reference Type 是什麼樣子,以 iOS 所使用的 Swift 為例:

UILabel 為 iOS 很常使用的 Class,通常用來顯示文字內容。

而當電腦執行到 var label = UILabel(),會做以下的事情:

1. 先在 RAM 中,找到 一塊 足夠大的,閒置的 memory 區塊。

2. 將 UILabel() 這個 instance,儲存在這個 memory 區塊中。

3. 再找到 一個 閒置的記憶體,將 label 這個變數與此記憶體位址關聯起來。

4. 將先前存放 instance 的 memory 區塊中第一個 memory address 儲存到 label 變數所對應的記憶體內。

5. 下次我們再使用到 label 這個變數的時候,電腦就會知道我們指的是這個 memory address 為起點的記憶體區塊,繼而去找到這個區塊以及其內所處存的 instance。

接著,var anotherLabel = label :

1. 找到 一個 閒置的記憶體。

2. 將 anotherLabel 這個變數與此記憶體位址關聯起來。

3. 將 label variable 中所儲存的資料,複製一份到 anotherLabel 中。

當我們使用一個變數,且此變數儲存的是 Reference Type 時,記憶體內儲存的是 記憶體區塊的 memory address。

因為 Class 描述的是一個物件,而一個物件的資料會超過單一記憶體所能容納的大小,因此我們需要的是 一塊 記憶體,而不是 一個 記憶體。這與 Value Type 只需要 一個 記憶體,且儲存 具體資料,有所不同。

而當我們需要這個 instance 的時候:

1. 將變數所對應之記憶體內,所儲存的 memory address 取出來。

2. 透過此 memory address,找到 instance,並對之做操作。

讓我們將兩個變數所存之 instance 的 text 印出來看看:

Console 將會印出:

Text in label variable: AppWorks School
Text in anotherLabel variable: AppWorks School
---
Text in label variable: Awesome swift!
Text in anotherLabel variable: Awesome swift!

labelanotherLabel 所儲存的記憶體位址 (0001230078901234) 相同,也就是透過這兩個變數,我們會找到同樣的 instance。anotherLabelinstance 做的行為,是可以透過 label 觀察到的。這與 Value Type,每個變數各自擁有獨立的資料,互不影響,是不一樣的運作模式。

Conclusion

Reference Type 與 Value Type 的差別,就是在記憶體內所儲存的內容,Reference Type 儲存 instance 的 記憶體位址,而 Value Type 儲存 資料本身,於是 Reference Type 的變數,會修改到同一個 instance,而 Value Type 不會。

Reference Type 的觀念,是學習寫程式的過程一定會碰到的東西,而記憶體與 Reference Type 的關聯,也是我寫程式一段時間之後,才有比較深刻的體悟。透過這篇文章,希望能給一樣剛接觸這領域的人一點幫助。

--

--