Swiftの値型と参照型、値渡しと参照渡し

Swift 5.0 において『値型(value type)と参照型(reference type)』、『値渡し(call by value)と参照渡し(call by reference, call by value result)』の振る舞いの違いを理解するためにサンプルコードを書いてみました。

Xcode 10.2のPlaygroundで動作を確認しています。

| &nbsp; | 値渡し | 参照渡し |
|--:|:---:|:---:|
| 値型 | 1 | 2 |
| 参照型 | 3 | 4 |

# 1. 値型の値渡し

```swift
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]
```

**【 ポイント 】**

1. Swiftでは配列は「**値型 (value type)**」である。
1. 配列oldArrayは関数 plusOne() を呼び出す前後で変化しない。
1. Swiftの関数引数は定数である。関数引数を直接書き換えることはできない。
1. Objective-Cでは配列(NSArray,NSMutableArray)は参照型である。



# 2. 値型の参照渡し(In-Out Parameter)

```swift
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]
```

**【 ポイント 】**


1. Swiftでは配列は『**値型 (value type)**』である。
1. In-Out引数は 『参照渡し(call by reference)』 または 『値渡しの結果返し(call by value result)』 となる。
1. 配列oldArrayは関数 plusOne() を呼び出す前後で変化する。



# 3. 参照型の値渡し

```swift
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"
}
```

**【 ポイント 】**

1. Swiftではクラスのインスタンスは『**参照型 (reference type)**』である。
1. 関数外の**fooCar**と関数内の**car1**は同じインスタンスを参照している。
2. 関数外の**barCar**と関数内の**car2**は同じインスタンスを参照している。
3. その結果、関数内で**car1**と**car2**のプロパティ(color)を書き換えると関数外の変数**fooCar**と**barCar**のプロパティも書き換わる。
4. fooCarとbarCarはmutateすることがないため、上記の例ではletが好ましい。
5. forCarとbarCarはインスタンスのプロパティがmutateするが、参照しているインスタンスそのものは変わっていない。



# 4. 参照型の参照渡し(In-Out Parameter)

```swift
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"
}
```

**【 ポイント 】**

1. Swiftではクラスのインスタンスは『**参照型 (reference type)**』である。
1. 参照渡しされた引数を直接書き換えている。
2. インスタンスの内容を書き変えたのではなく、引数（変数）が参照している先（インスタンス）を差し替えている。
3. 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