Swift 5.0 において『値型(value type)と参照型(reference type)』、『値渡し(call by value)と参照渡し(call by reference, call by value result)』の振る舞いの違いを理解するためにサンプルコードを書いてみました。
Xcode 10.2のPlaygroundで動作を確認しています。
値渡し | 参照渡し | |
---|---|---|
値型 | 1 | 2 |
参照型 | 3 | 4 |
1. 値型の値渡し
func plusOne(array : [Int]) -> [Int] {
var array = array
for i in 0 ..< array.count {
array[i] = array[i] + 1
}
return array
}
var oldArray = [0, 1, 2]
print(oldArray) // => [0, 1, 2]
var newArray = plusOne(array: oldArray) // => [1, 2, 3]
print(oldArray) // => [0, 1, 2]
【 ポイント 】
- Swiftでは配列は「値型 (value type)」である。
- 配列oldArrayは関数 plusOne() を呼び出す前後で変化しない。
- Swiftの関数引数は定数である。関数引数を直接書き換えることはできない。
- Objective-Cでは配列(NSArray,NSMutableArray)は参照型である。
2. 値型の参照渡し(In-Out Parameter)
func plusOne(array : inout [Int]) -> [Int] {
for i in 0 ..< array.count {
array[i] = array[i] + 1
}
return array
}
var oldArray = [0, 1, 2]
print(oldArray) // => [0, 1, 2]
var newArray = plusOne(array: &oldArray) // => [1, 2, 3]
print(oldArray) // => [1, 2, 3]
【 ポイント 】
- Swiftでは配列は『値型 (value type)』である。
- In-Out引数は 『参照渡し(call by reference)』 または 『値渡しの結果返し(call by value result)』 となる。
- 配列oldArrayは関数 plusOne() を呼び出す前後で変化する。
3. 参照型の値渡し
class Car {
var color : String
init(color : String) {
self.color = color
}
}
func swap_color(car1: Car, car2: Car) {
let tmp = car1.color
car1.color = car2.color
car2.color = tmp
}
do {
let fooCar = Car(color: "Red")
let barCar = Car(color: "Blue")
print(fooCar.color) // => "Red"
print(barCar.color) // => "Blue"
swap_color(car1: fooCar, car2: barCar)
print(fooCar.color) // => "Blue"
print(barCar.color) // => "Red"
}
【 ポイント 】
- Swiftではクラスのインスタンスは『参照型 (reference type)』である。
- 関数外のfooCarと関数内のcar1は同じインスタンスを参照している。
- 関数外のbarCarと関数内のcar2は同じインスタンスを参照している。
- その結果、関数内でcar1とcar2のプロパティ(color)を書き換えると関数外の変数fooCarとbarCarのプロパティも書き換わる。
- fooCarとbarCarはmutateすることがないため、上記の例ではletが好ましい。
- forCarとbarCarはインスタンスのプロパティがmutateするが、参照しているインスタンスそのものは変わっていない。
4. 参照型の参照渡し(In-Out Parameter)
class Car {
var color : String
init(color : String) {
self.color = color
}
}
func swap(car1: inout Car, car2: inout Car) {
let tmpCar : Car
tmpCar = car1
car1 = car2
car2 = tmpCar
}
do {
var fooCar = Car(color: "Red")
var barCar = Car(color: "Blue")
print(fooCar.color) // => "Red"
print(barCar.color) // => "Blue"
swap(car1: &fooCar, car2: &barCar)
print(fooCar.color) // => "Blue"
print(barCar.color) // => "Red"
}
【 ポイント 】
- Swiftではクラスのインスタンスは『参照型 (reference type)』である。
- 参照渡しされた引数を直接書き換えている。
- インスタンスの内容を書き変えたのではなく、引数(変数)が参照している先(インスタンス)を差し替えている。
- fooCarとbarCarはmutateするためvarで宣言する必要がある。
参照
- Apple Developer Blog "Value and Reference Types" : https://developer.apple.com/swift/blog/?id=10
- Swift "In-Out Parameters" : https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID545