以下資料皆參考自Ref1,這裡僅作個人學習整理:
- Definition of Value type and Reference type
- When do we choose a Value Type over a Reference Type?
- How Reference and Value types are stored in memory ?
Definition of Value type and Reference type:
Value type definition:
當Value type被assign給變數或傳遞給function時,就會把既有的instance複製一份出去。
Note:
IOS primitive collection types such as array, set, dictionary採用copy-on-write機制,所以只有把前述collection types單純assign給變數或傳遞給function,但未對「值」做修改的話,則新的變數還是指到同一個memory address
Sample code:
struct MyStruct {
var tmp: Int
}
var a = MyStruct(tmp: 1)
var b = a
print(MemoryAddress(of: &a)) //0x0000000100008398
print(MemoryAddress(of: &b)) //0x00000001000083a0
var arr1 = Array(repeating: 1, count: 9)
var arr2 = arr1
print(MemoryAddress(of: &arr1)) //0x00000001028047a0
print(MemoryAddress(of: &arr2)) //0x00000001028047a0
Reference type definition:
當Reference type被assign給變數或傳遞給function時,只會把reference copy一份給新變數或function,所以新變數仍然指到同一個object。
Sample code:
class MyClass {
var tmp: Int
init(_ tmp: Int) {
self.tmp = tmp
}
}
var c = MyClass(1)
var d = c
print(MemoryAddress(of: c)) //0x000000010062b930
print(MemoryAddress(of: d)) //0x000000010062b930
When do we choose a Value Type over a Reference Type?
使用Value type時機:
- 用「 == 」 去比較兩個instance data比較合理時(p.s. double equal operator (aka ==)是用來比較value)
- 希望新的變數擁有獨立的state
- Data會在multiple threads中使用,不需要擔心在目前thread上的data會被別的thread修改
使用Reference type時機:
- 用「 === 」去比較兩個instance比較合理時 (p.s. ===是根據memory address來比較兩個objects是否相同)
- 你希望產生一個分享且可修改的state
How Reference and Value types are stored in memory ?
- Value Type — 存在stack上
- Reference Type — 存在heap上
Note:
Reference type的「reference」是存在stack上,而object instance是存在heap上
補充:
The way to prinit struct/class memory address [參考自Ref3]:
struct MemoryAddress<T>: CustomStringConvertible {
let intValue: Int
var description: String {
let length = 2 + 2 * MemoryLayout<UnsafeRawPointer>.size
return String(format: "%0\(length)p", intValue)
}
// for structures
init(of structPointer: UnsafePointer<T>) {
intValue = Int(bitPattern: structPointer)
}
}
extension MemoryAddress where T: AnyObject {
// for classes
init(of classInstance: T) {
intValue = unsafeBitCast(classInstance, to: Int.self)
// or Int(bitPattern: Unmanaged<T>.passUnretained(classInstance).toOpaque())
}
}
Ref: