teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

3つ目の質問に対する例題を追記

2020/03/21 16:08

投稿

TsukubaDepot
TsukubaDepot

スコア5086

answer CHANGED
@@ -111,5 +111,81 @@
111
111
  > "「soundPlayer」処理後にシンバルの情報を呼び出し元に戻して利用したいので、参照渡しにしている"
112
112
  > と記載されています。実際には&なしでコーディングしても結果、動作させても影響がない様に思えるのですが、具体的にどの様な影響があり、なぜ参照渡しにすべきなのかアドバイスをを頂戴できると大変嬉しいで す。
113
113
 
114
+ 私が少し勘違いと混乱していたようです。
115
+ `&`を取ってコンパイルするとエラーになるはずですが、質問者さんの環境ではいかがでしょうか。
116
+
117
+ ![&の不足](72c52d5fa4b23b8520eb2e08bc4601ec.png)
118
+
114
- これは質問者さんのご指摘は尤もか思いますが、まだ自分でも納得のゆくよう説明方法見つかりません
119
+ したがって具体的な影響しては、まず「コンパイルに通らい」という影響りま
120
+
121
+ 理由ですが、英語による Swift のマニュアルのうち、[In-Out Parametersの項](https://docs.swift.org/swift-book/LanguageGuide/Functions.html#ID173)をみるとこのように記述してあります。
122
+
123
+ > You place an ampersand (&) directly before a variable’s name when you pass it as an argument to an in-out parameter, to indicate that it can be modified by the function.
124
+
125
+ また、参考文献[1]では「ここで使っている`&`という記号は通常の演算子ではなく、`inout`引数に対応する実引数で表す記法です」とあり(p.44)、参考文献[2]では「インアウト引数を持つ関数を呼び出すには、インアウト引数の先頭に`&`を加えます」とあります(p.110)。
126
+
127
+ C言語をやっていたらアドレス演算子としての印象が強く、`&`という記号そのものに何らかの操作があるように感じます(し、私の場合はついその印象で考えてしまいました)。
128
+
129
+ しかし英文マニュアルの説明などを見る限り、Swiftでは`&`を付与することによって特別な操作があるわけではなく、`&`をつけることによって、引数として与えられた変数が書き換えられるということを明示的に示していると考えられます。
130
+
131
+ では、そもそも何故値渡しではなく`&`をつけて参照渡しするのかというと、処理先の関数の中で実引数として渡したプロパティが参照しているインスタンスそのものを書き換えているからです。
132
+
115
- もうちょっと考えてみますが、とりあえず上記2点だけ先に回答しおき
133
+ あんり良い例題ではないですが、こんなサンプルを作っした
134
+
135
+ ```swift
136
+ import UIKit
137
+
138
+ class ClassA {
139
+ var msg: String
140
+ init(msg: String) {
141
+ self.msg = msg
142
+ }
143
+ deinit {
144
+ print("deinit - msg: (msg)")
145
+ }
146
+ }
147
+
148
+ func newClass(cl: ClassA){
149
+ // Swift は仮引数の値は書き換えられないので、同名の別の変数に置き換える
150
+ // 参考文献[1]p.46
151
+ var cl = cl
152
+ cl = ClassA(msg: "newClass内で作ったインスタンス")
153
+ }
154
+
155
+ func newClassWithInout(cl: inout ClassA){
156
+ cl = ClassA(msg: "newClassWithInout内で作ったインスタンス")
157
+ }
158
+
159
+ do{
160
+ print("inout 無しで呼び出し")
161
+ var clA = ClassA(msg: "最初のインスタンス")
162
+ print("newClass呼び出し前 - msg: (clA.msg)")
163
+ newClass(cl: clA)
164
+ print("newClass呼び出し後 - msg: (clA.msg)")
165
+ }
166
+
167
+ print("")
168
+
169
+ do{
170
+ print("inout 付きで呼び出し")
171
+ var clA = ClassA(msg: "最初のインスタンス")
172
+ print("newClass呼び出し前 - msg: (clA.msg)")
173
+ newClassWithInout(cl: &clA)
174
+ print("newClass呼び出し後 - msg: (clA.msg)")
175
+ }
176
+ ```
177
+ 実行すると、こんな感じになると思います。
178
+ ```
179
+ inout 無しで呼び出し
180
+ newClass呼び出し前 - msg: 最初のインスタンス
181
+ deinit - msg: newClass内で作ったインスタンス
182
+ newClass呼び出し後 - msg: 最初のインスタンス
183
+ deinit - msg: 最初のインスタンス
184
+
185
+ inout 付きで呼び出し
186
+ newClass呼び出し前 - msg: 最初のインスタンス
187
+ deinit - msg: 最初のインスタンス
188
+ newClass呼び出し後 - msg: newClassWithInout内で作ったインスタンス
189
+ deinit - msg: newClassWithInout内で作ったインスタンス
190
+ ```
191
+ 注意深く読まないと分かりにくいとおもいますし、もっと細かく`print()`をいれて動作を確認する必要はあると思いますが、`inout`あり無しの処理において、最初に作ったインスタンスと、関数内で新たに作ったインスタンスのライフサイクルの違いはわかるかと思います。