MENU

Swiftの引数パズル: 値渡し vs. 参照渡し

値渡しと参照渡しの概念を説明するブログのヘッダー画像

プログラミングでは、変数の扱い方がプロジェクトの成功を左右します。
特に、Swiftのような言語でソフトウェアを開発する際、値渡しと参照渡しの違いを理解することは不可欠です。

あなたは、関数に変数を渡すとき、その変数の「本物」を渡しているのか、それともただの「コピー」を渡しているのか、その違いがプログラムにどのような影響を与えるかを考えたことがありますか?

この記事では、値渡しと参照渡しの概念を明確にし、Swiftでこれらの概念をいかに駆使して効率的かつエラーのないコードを書くかを、サンプルコードを用いて解説します。
さらに、PCのファイル操作に例えて直感的に理解できるようにも導きます。

あなたのSwiftプログラミングスキルを次のレベルに引き上げるための知識を、わかりやすく解説していきます。

目次

値渡し

値渡しサンプルコード

func nameFunc1(name1: String) {
    let name1 = "初期値"
    print(name1)
}

var res1 = "変更値"
print(res1) 
nameFunc1(name1: res1) 
print(res1) 

結果

変更値
初期値
変更値

プログラムの実行順序に解説

  1. 6行目
    変数res1が宣言されて、res1に”変更値”が代入(初期化)されます。
  2. 7行目
    コンソールにrest1の値”変更値”が出力
  3. 8行目
    nameFunc1(name1: res1)が関数func nameFunc1(name1: String) を呼び出します。
  4. 8行目、1行目
    8行目のrest1の値”変更値”が1行目のname1に渡されて、1行目のname1の値は”変更値”になります。(値渡し
    渡されるとは代入またはコピーされるという意味です。
  5. 2行目
    関数内にある定数name1が宣言されて、name1に”初期値”を代入
    ここで注意すべき2行目のname1と8行目のname1はスコープが異なるため別物です。
    2行目のname1の値は”初期値“で8行目のname1の値は”変更値”です。
    メモリー上のアドレスが違う
    と考えて下さい。
  6. コンソールにrest1の値”変更値”が出力

参照渡し

参照渡しサンプルコード

func nameFunc2(name2: inout String) {
    name2 = "初期値"
    print(name2)
}

var res2 = "変更値"
print(res2) 
nameFunc2(name2: &res2) 
print(res2) 

結果

変更値
初期値
初期値

プログラムの実行順序に解説

  1. 6行目
    変数res2が宣言されて、res2に”変更値”が代入(初期化)されます。
  2. 7行目
    コンソールにrest2の値”変更値”が出力
  3. 8行目
    nameFunc1(name1: &res2)が関数func nameFunc2(name2: String) を呼び出します。
  4. 8行目、1行目
    8行目のrest2のアドレスが1行目のname2に渡されて同じアドレスになります。(参照渡し
    rest2の値”変更値”は渡されません。
  5. 2行目
    関数内にある定数name2が宣言されて、再定義されています。併せてname2に”初期値”を代入
    1行目のname2にはinoutキーワードが付いているので関数内にあるname2と同一になります。
  6. 3行目
    コンソールに2行目のname2の値”初期値”が出力
  7. 9行目
    rest2はname2と同じアドレスのためname2の値”初期値”が代入されています。そのためコンソールには”初期値”が出力
「&」記号と「inout」キーワード

Swiftにおける&記号は、関数に引数を渡す際にinoutパラメータとしてその引数のメモリアドレスを直接渡すことを意味します。

inoutキーワードが付いたパラメータは、関数の外部にある変数の値を関数内で変更できるようにするために使用されます。この機能により、関数は引数として受け取った変数の実際の値を直接更新することが可能になります。

  • &使用法
    inoutパラメータを持つ関数を呼び出す際、対象の変数の前に&を置きます。これにより、Swiftはその変数のメモリアドレスを関数に渡すことができます。
  • &意味
    &は、変数の「参照」を関数に渡すことを意味します。これにより、関数内で行われる変数の変更は、関数の呼び出し元においても反映されます。

値渡しと参照渡しの違いを比較

項目値渡し参照渡し
仕組み変数の値を渡す変数のメモリアドレスを渡す
メリットメモリ効率が良いコードの簡潔化
デメリット関数内で変更しても元の変数には反映されないメモリ管理が複雑

スッキリと理解できない場合の直感的な理解方法

プログラミングにおける参照渡し値渡しの違いを理解するために、Windowsのファイル管理における「ファイルのショートカットを作る」行為と「ファイルのコピーを作る」行為との比較で理解してみましょう。

ただし、このアナロジーは、プログラミングの概念を視覚的に理解するのに役立ちますが、実際のメモリ管理や変数の扱いにおいては、プログラミング言語の具体的な挙動や安全性の観点から、より複雑な要素が関わってくることを理解しておくことが重要です。

値渡し(ファイルのコピー)

値渡しは、ある変数の内容(値)を別の変数にコピーするプロセスです。
これは、ファイルをコピーして新しい場所に配置する行為に似ています。

コピーされたファイルは元のファイルと全く同じ内容を持ちますが、それぞれが異なる場所(メモリアドレス)に存在します。このため、一方を変更しても、もう一方には影響しません。

参照渡し(ファイルのショートカット)

参照渡しは、変数の実際のメモリアドレス(参照)が渡されます。
これは、ファイルのショートカットを作る行為と類似しています。

ショートカットは元のファイルへのポインタ(リンク)のようなもので、ショートカットを通じて元のファイルが開かれます。
参照渡しの場合、関数やメソッド内で変数の値を変更すると、その変更は元の変数に反映されます。これは、ショートカットを介してファイルを編集すると、その変更が元のファイルにも適用されることに相当します。

FAQ

値渡しとは何ですか?

値渡しは、関数に引数として渡された変数のコピーを作成するプロセスです。
関数内でこのコピーに対する変更は、元の変数には影響しません。
これは、あるファイルをコピーして新しい場所に配置する行為に似ています。

参照渡しとは何ですか?

参照渡しは、関数に変数のメモリアドレスが直接渡されます。これにより、関数内での変更が渡された変数に直接反映されます。
ファイルのショートカットを作る行為と類似しており、ショートカットを介して行われる変更が元のファイルにも適用されることに相当します。

参照渡しをSwiftの関数でおこなうには、どうすればいいですか?

inout」キーワードを関数のパラメータに使用し、関数を呼び出す際に変数の前に「&」記号を付けて渡します。これにより、関数内での変更が呼び出し元の変数にも反映されます。

値渡しと参照渡しのどちらを使うべきですか?

値渡しは計算や比較など、元の変数を変更しない処理で使用します。
一方、参照渡しはデータの更新や追加など、元の変数を変更する処理で使用します。
プログラムの要件と目的に応じて適切な方を選択することが重要です。

関数内でパラメータを再定義する場合の注意点は?

再定義した場合、呼び出し元の変数とは別のローカル変数を指すことになります。つまり別名で定義したことと同じになります。
値渡し参照渡しかに関わらず、この点には注意が必要です。

まとめ

関数に引数を渡す際、Swiftでは値渡し参照渡しの2通りの方法があります。値渡しでは引数の値自体がコピーされて関数に渡され、参照渡しでは引数のメモリアドレスが渡されます。

値渡しの場合、コピーを渡すので関数内で引数を変更しても呼び出し元の変数には影響しません。
参照渡しの場合は影響が及び、呼び出し元の変数も変更されます。

参照渡しを行うには、関数のパラメータに「inout」キーワードを指定し、呼び出し時には変数の前に&を付けます。
これによりその変数への参照が渡されます。

関数内でパラメータを再定義すると、値渡しか参照渡しかに関わらず、呼び出し元の変数とは別のローカル変数を指すことになります。混乱を避けるため、再定義は避けて別名にすることをおすすめします。

値渡しと参照渡しの違いを理解する一助として、Windowsのファイル操作を例に考えることができます。
値渡しはファイルのコピーのように元とは独立した別物であり、参照渡しはショートカットのように元への参照を渡しているとイメージすることができます。

目次