Edited at

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

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で宣言する必要がある。


参照