iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら。
今回は、Class と Structure に関して。
Classes, Structures
前回 の続きとして、Array と Dictionary のコピー・代入される際の挙動に関して。特に配列の挙動はちょっと特殊です。
4. Dictionaryの代入とコピー
Dictionary が変数やコンスタントに代入された時、また関数やメソッドの引数として渡された時には、キー/値はコピーして渡されます。キーや値がリファレンスタイプの時は、そのリファレンスがコピーされます。
1 2 3 4 5 6 | var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] var copiedAges = ages copiedAges["Peter"] = 24 println(ages["Peter"]) // prints "23" |
5. 配列の代入とコピー
配列の代入とコピーはちょっと複雑です。配列が、変数やコンスタントに代入されるか、関数やメソッドの引数として渡された場合、”この時点”では配列はリファレンスとして渡されます。つまり、同じメモリ領域をシェアしています。配列の要素の値を変更したら、他のリファレンスしている変数等から確認することができます。下記の例では、a[0]を変更すると、b や c からも変更を確認することができます。
1 2 3 4 5 6 7 8 9 10 11 | var a = [1, 2, 3] var b = a var c = a a[0] = 42 println(a[0]) // 42 println(b[0]) // 42 println(c[0]) // 42 |
しかし、配列の長さを変える(要素の削除、追加)を伴う時には、配列がコピーされます。この実際に配列の長さを変更した際に、コピーされます。下記の例では、a 配列の長さを append() で変えています。そのため、b と c はこの時点で 古い a (a -> (1, 2, 3)) のコピーを参照しています。したがって、a の値を変更しても b と c からは変更を確認することができません。ちなみに、この b と c は同じ配列のリファレンスを持っています。
1 2 3 4 5 6 7 8 | a.append(4) a[0] = 777 println(a[0]) // 777 println(b[0]) // 42 println(c[0]) // 42 |
上記でみたように、いくつかの変数が同じ配列へのリファレンスを持っていると、ひとつの変数から配列に変更を与えると、他のリファレンスから参照した場合にも、その変更が適応されていまいます。関数等に配列を渡す際に、その変数が持つリファレンスがプログラムの中で唯一の参照元であるということを確認する方法があります。それが、unshare メソッドです。このメソッドは、変数にのみ使用できます。
1 | b.unshare() |
現在、b と c は同じリファレンスを持っています。そのため、b を変更すると c からその配列を参照した際に、b 側で行った変更が、c 側からも確認できます。これが好ましくない場合に、この unshare メソッドを使用します。この操作により、b へ新しいこの配列がコピーされ、b で b が持つ配列を変更しても、c へは影響を与えません。
1 2 3 4 5 6 7 | b[0] = -105 println(a[0]) // 777 println(b[0]) // -105 println(c[0]) // 42 |
ある 2 つの配列を参照してる変数に対して、同じ配列を参照しているかをチェックするには、===/!== オペレーターを使用します。
1 2 3 4 5 6 | if b === c { println("b and c still share the same array elements.") } else { println("b and c now refer to two independent sets of array elements.") } // prints "b and c now refer to two independent sets of array elements." |
配列が代入された時点で(長さを変えた時点ではなく)、配列のコピーを実行して欲しい場合には、copy メソッドを使用します。
1 2 3 4 5 6 | var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"] var copiedNames = names.copy() copiedNames[0] = "Mo" println(names[0]) // prints "Mohsen" |
先ほどの unshare メソッドは、その変数が唯一の参照元の場合には、コピーを作成しません。しかし、この copy メソッドは、常に新しいコピーを作成します。そのため、もし単にリファレンスがプログラムの中で唯一のものあることを確認した場合には、unshare メソッドを使用した方が効率的です。