SwiftのArrayのハマりどころのまとめ

Swiftも1.0がリリースされ、SwiftでiOSアプリを申請できるようになりました。Swiftはとても学びやすい言語ですが、 _Array_ （や _Dictionary_ ）の挙動については最初戸惑う人が多いのではないかと思います。そこで、Swiftを学習するときに _Array_ についてハマりそうな点をまとめました。

多くの言語では配列やリストは __参照型（Reference Type）__ として扱われますが、 Swiftの _Array_ は __値型（Value Type）__ です。配列やリストが参照型である言語（Objective-C、C#、Java、JavaScript、Python、Ruby、…）に慣れ親しんでいる人ほど、この違いに戸惑うのではないかと思います。

本稿では __Arrayが値型であることによってハマりがちなポイントについてまとめます__ 。個別のメソッドの使い方などでハマりがちな点については対象としませんのであしからず。

なお、ここでは取り上げませんが _Dictionary_ については全く同じことが言えます。

同じArrayを参照しているとかんちがいする
----------------------------------

Swiftの _Array_ は次のような挙動を示します。

```swift:A
var a = [1, 2, 3]
var b = a
b[0] = 999 // a[0]は999ではなく1のまま
```

多くの言語の配列では _a\[0\]_ も _999_ になります。そのような配列に慣れているとSwiftの _Array_ の挙動は不自然に思えるかもしれません。しかし、それは参照型の配列に慣れすぎただけです。本来は、参照を共有している状態の方が不自然なはずです（詳細は後述）。

Swiftの _Array_ は値型なので __代入時には _Array_ が丸ごとコピー__ されます。 _b = a_ の時点で _Array_ はコピーされるので、その後で _b_ を変更しても _a_ には何も影響を与えません。

Arrayがvarとletでミュータブルやイミュータブルに変化すると考える
-------------------------------------------------------

Swiftの _Array_ は __varで宣言するとミュータブル（可変）に、letで宣言するとイミュータブル（不変）__ になります。

```swift:B
var a = [1, 2, 3]
a[0] = 999 // OK

let b = [1, 2, 3]
b[0] = 999 // コンパイルエラー！
```

では、 _let_ で宣言したイミュータブルな _Array_ を _var_ に代入して変更するとどうなるでしょう？

```swift:C
let a = [1, 2, 3]
var b = a
b[0] = 999
```

これについても前節のように、 _b = a_ の時点で _Array_ はコピーされいるのでその後 _b_ に変更を加えても _a_ には関係ありません。 _a\[0\]_ は _1_ のままです。 ___var_ か _let_ かでオブジェクトがミュータブルかイミュータブルかが変化しているわけではない__ のです。 _b_ は _a_ から要素をコピーして作られた、 _a_ とは別のミュータブルな _Array_ になります。

_var_ で宣言した _Array_ を _let_ に代入した場合も同様です。

```swift:D
var a = [1, 2, 3]
let b = a
a[0] = 999 // b[0]は1のまま
```

Arrayを引数に渡して変更しようとする
------------------------------

 _Array_ を関数やメソッドに渡す場合も注意が必要です。

```swift:E
// Arrayの要素をすべて2倍する関数
func makeDouble(var array: [Int]) {
	for var i = 0; i < array.count; i++ {
		array[i] *= 2
	}
}

var a = [1, 2, 3]
makeDouble(a)
println(a) // [1, 2, 3]のまま
```

上記の関数 _makeDouble_ は参照型の配列にとってはごく自然な実装ですが、Swiftの _Array_ では正しく動きません。

 _Array_ は値型なので、 __引数に渡される場合もコピー__ されます。コピーされた _Array_ を変更してもそれは元の引数に渡された _Array_ とは関係ありません。 _Int_ を引数に渡して、それを関数の中で変更しても元の値は変更されないのと同じです。

### 関数やメソッドにArrayを渡して変更する方法

では、関数やメソッドに渡された _Array_ を変更するにはどう書けば良いでしょうか。

Swiftでは、 __引数で渡された値に変更を加える場合は _inout_ という特別なキーワードを使います__。

```swift:F
// Arrayの要素をすべて2倍する関数
func makeDouble(inout array: [Int]) { // inoutを付ける
	for var i = 0; i < array.count; i++ {
		array[i] *= 2
	}
}

var a = [1, 2, 3]
makeDouble(&a) // &をつける
println(a) // [2, 4, 6]
```

_inout_ が付与された引数は、関数・メソッドの中で変更された場合にその変更が呼び出し元に反映されます。また、 _inout_ な引数を持った関数・メソッドをコールするときには値に _&_ をつける必要があります。これはC言語でポインタを渡すときと同じなのでC/C++経験者には親しみやすいと思います。 _inout_ は値の参照をとって関数・メソッドに渡しているようなイメージだととらえると、 _Array_ を渡して変更できる意味がわかりやすいと思います。

ただし、 _inout_ で渡された値であっても関数の中からはただの値にしか見えませんし、参照として保持しておくこともできません。そのため、 ___inout_ を使っても _Array_ の参照をとって共有することはできません__ 。

どうしても _Array_ を共有したければ、 _Array_ をラップしたクラスを作ってそのインスタンスを共有することはできます。参照型の配列に慣れているとそういうことを考えてしまいがちですが、よくよく考えてみると ___Array_ そのものを共有しなければならないケースはほとんどない__ と思います（詳細は後述）。

オブジェクトの内外でArrayを共有しようとする
-------------------------------------

クラスに _Array_ を保持するにも、参照型の配列とは勝手が違います。

_Array_ を保持するクラスの例として、SNSのようなサービスを考えて、ユーザーを表す _User_ クラスと、ユーザーが所属する _Group_ クラスを考えてみましょう。 _Group_ クラスは所属する _User_ の _Array_ を持ちます。

```swift:G
class User{}
class Group{
	var users: [User]
	init(users: [User]) {
		self.users = users
	}
}

var users = [User(), User(), User()]
let group = Group(users: users)

users.append(User()) // group.usersには追加されない
```

このようにオブジェクトに渡した _Array_ を後から変更しても、オブジェクトの保持している _Array_ には影響を与えません。これも、 __イニシャライザに渡すときに _Array_ がコピーされる__ からです。イニシャライザではなく、 __プロパティやメソッドに _Array_ を渡す場合も同様__ です。

また、 __オブジェクトから受け取った _Array_ を変更した場合も、 _return_ および代入時にコピーされるのでオブジェクト内の _Array_ には影響を与えません__ 。

```swift:H
var groupUsers = group.users
groupUsers.append(User()) // group.usersには追加されない
```

余談ですが、__G__ のコード中で ___group_ は _let_ で宣言されていますが、代入された _Group_ オブジェクトがイミュータブルになるわけではない__ ことに注意して下さい。 _Group_ は _Array_ とは違いクラスです。 ___let_ でも _group_ に格納されたオブジェクトへの参照が不変になる（ _group_ に再代入できなくなる）だけ__ で、オブジェクトが不変になるわけではありません。 _let_ はJavaで変数宣言に _final_ をつけているのと同じです。

### オブジェクトが保持しているArrayの変更する方法

では、オブジェクトが保持している _Array_ を変更するにはどうすれば良いでしょう？例えば、次のようにします。

```swift:I
group.users.append(User()) // group.usersに追加される
```

あるいは、 _Group_ クラスにユーザーを追加するメソッドを実装しても良いでしょう。

```swift:J
class Group{
	var users: [User]
	init(users: [User]) {
		self.users = users
	}
	// ユーザーを追加するメソッド
	func appendUser(user: User) {
		users.append(user)
	}
}

var users = [User(), User(), User()]
let group = Group(users: users)

group.appendUser(User()) // group.usersに追加される
```

### オブジェクトの内外でArrayを共有する方法

どうしても _Array_ を共有したければ、前述のように、 _Array_ をラップしたクラスを作ってそのオブジェクトを共有することで、間接的に _Array_ を共有することができます。

しかし、よく考えてみて下さい。本当にオブジェクトの内外や複数のオブジェクト間で _Array_ と _Array_ への変更を共有したいようなケースがあるでしょうか。 __カプセル化されたオブジェクトの内部状態をメソッドを介さずに変更できるのは望ましい設計ではありません__ 。

たとえオブジェクトの内外で共有しなくても、 _Array_ を複数の変数に格納して、片方を変更するともう片方も変更されてうれしいのはどんなケースでしょうか。単に、参照型の配列に慣れすぎて、Swiftの _Array_ の挙動が不自然に感じているだけではないでしょうか。 _Array_ を共有したいという気持ちになったときは、設計に問題がないか振り返ってみるのがいいと思います。（こんなケースではどうしても _Array_ を共有しなければらないというケースがあれば教えてもらえるとうれしいです。）

パフォーマンスのためにArrayのコピーを避けようとする
--------------------------------------------

そもそも、多くの言語ではなぜ配列は参照型として実装され、オブジェクトが共有されるのでしょうか。それは、代入の度に配列が丸ごとコピーされたのではパフォーマンスが悪すぎて問題だからです。すると、Swiftでもパフォーマンスのために _Array_ のコピーを避ける（例えば、前述のように _Array_ をラップしたクラスを作ってそのインスタンスを共有する）必要があるのでしょうか。

結論から言うと、Swiftでは ___Array_ のコピーによるパフォーマンスへの影響を気にする必要はほぼありません__。

確かに、概念的にはこれまで見てきた通り、

- 代入されるとき
- 引数に渡されるとき
- 戻り値として _return_ されるとき

のすべてで _Array_ はコピーされます。プログラムの実行結果はそのように考えた通りになります。

しかし、いくらなんでも本当に上記のケースで毎回 _Array_ をコピーしていたら、パフォーマンスが悪すぎて使い物になりません。 __Swiftの _Array_ は不要なコピーを行わないように最適化されており、実際にはコピーはほぼ実行されません__ 。結果が変わらない範囲でコピーをさぼって、コピーしないと不都合が起こるときだけコピーするのです。

具体例を挙げます。次のコードの1〜4のどこでコピーが発生するでしょう？

```swift:K
// 100万個の要素を持つArray
var a: [Int] = ([Int])(1...1000000)

var b = a // 1
println(b[0]) // 2
a[0] = 888 // 3
a[0] = 999 // 4
```

Swiftの _Array_ は値型ですが、内部では実体への参照を保持しています。  _Array_ を代入すると実体への参照だけがコピーされ、不都合が生じるまでは実体が共有されます。 __K__ の例では次のようになります。

1. 代入したが、この時点ではコピーせずに実体を共有
2. _b[0]_ を取り出すだけなら _a_ と _b_ で同じ実体を共有していても問題なし
3. _a\[0\]_ が変更され _a_ と _b_ は異なる _Array_ である必要が生じたのでここで実体をコピー
4. 再度 _a_ を変更しても、 _a_ の実体は誰とも共有されていないのでコピーは不要

同じように、関数やメソッド、イニシャライザの引数に渡してもコピーは発生しません。

```swift:L
func sum(numbers: [Int]) -> Int {
	var sum = 0
	
	for number in numbers {
		sum += number
	}

	return sum
}
println(sum(a)) // aの実体を共有しても問題ないのでコピーは発生しない
```

関数の戻り値として _Array_ を _return_ してもコピーは発生しません。

```swift:M
 // 与えられた値までのArrayを作成する関数
func numbers(count: Int) -> [Int] {
	return ([Int])(1...count)
}

var c = numbers(1000000) // returnして代入してもコピーは発生しない
c.append(1000001) // 変更してもコピーは発生しない
```

上記のコードでは、 _return_ された _Array_ に変更を加えていますが、その時点で関数のスコープは抜けているため、この _Array_ の実体は他から参照されていません。そのためやはりコピーは発生しません。

このように、 _Array_ は複数箇所から実体を共有されない限り実際にコピーされることはありません。よほどおかしな設計でなければ不要にコピーが発生してパフォーマンスが劣化するようなことはないでしょう。Swiftにおいては、 __コピーを避けるために _Array_ を共有しようとするのは不自然でわかりづらいコードに生むだけ__でメリットはないと思います。

_Array_ のコピーについて[より詳しくはこちら](http://qiita.com/koher/items/47f444200d4e02b6414e)をご覧下さい。

CovariantなArrayが型安全性を破壊していると思う
-----------------------------------------

ハマりどころとは少し違いますが、 _Array_ が値型であることに起因した挙動なので挙げておきます。

一見おかしなように感じますが、Swiftの _Array_ はミュータブルだけどCovariantです。それでいて型安全です。[詳しくはこちら](http://qiita.com/koher/items/81767a345d5ed53c04b7)をご覧下さい。

まとめ
-----

多くの言語と違いSwiftの _Array_ は値型であるため、配列が参照型である言語に慣れ親しんだ人にとって直観と異なる挙動を示します。そのことによってハマりがちなポイントをまとめました。

最初は不自然に思えるかもしれませんが、 _Array_ が値型であるということを理解してしまえば一貫性を持ったシンプルな挙動であることがわかりますし、むしろ _Array_ は値型である方が自然で望ましいのではないかと思えてきます。