LoginSignup
84
66

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-19

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]

【 ポイント 】

  1. Swiftでは配列は「値型 (value type)」である。
  2. 配列oldArrayは関数 plusOne() を呼び出す前後で変化しない。
  3. Swiftの関数引数は定数である。関数引数を直接書き換えることはできない。
  4. 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]

【 ポイント 】

  1. Swiftでは配列は『値型 (value type)』である。
  2. In-Out引数は 『参照渡し(call by reference)』 または 『値渡しの結果返し(call by value result)』 となる。
  3. 配列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"
}

【 ポイント 】

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

【 ポイント 】

  1. Swiftではクラスのインスタンスは『参照型 (reference type)』である。
  2. 参照渡しされた引数を直接書き換えている。
  3. インスタンスの内容を書き変えたのではなく、引数(変数)が参照している先(インスタンス)を差し替えている。
  4. fooCarとbarCarはmutateするためvarで宣言する必要がある。

参照

84
66
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
84
66