値型と参照型

値型
参照型
で、どっちを使えばいいの
コレクションの挙動

値型

例によって構造体と列挙型は値型に属する。IntやFloatやBool、String、Array、Dictionaryも値型。

struct Resolution {
	var width = 0
	var height = 0
}
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

構造体Resolutionから定数hdをつくって、それをcinemaに代入してみた。というわけでcinemaもhdと同様widthが1920・heightが1080の構造体Resolutionである。だが、構造体は値型なので、瓜二つであっても別物である。

cinema.width = 2048

//hdとcinemaは別人なので違う値が出る
//cinema is now 2048 pixels wide
println("cinema is now \(cinema.width) pixels wide")
//hd is still 1920 pixels wide
println("hd is still \(hd.width) pixels wide")

列挙型だとこんな感じ。currentDirectionとrememberedDirectionは別人である。

enum CompassPoint {
	case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East

//The remembered direction is still .West
if rememberedDirection == .West {
	println("The remembered direction is still .West")
}

参照型

クラスは参照型です。

class VideoMode {
	var resolution = Resolution()
	var interlaced = false
	var frameRate = 0.0
	var name: String?
}

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

ここまではなんともない定数tenEightyなわけだが、これをalsoTenEightyに代入し、alsoTenEightyのframeRateを書き換えてみる。

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

クラスは参照型なので、tenEightyもそれを代入したalsoTenEightyも同一人物である。

//The frameRate property of tenEighty is now 30.0
println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")

「定数なのにtenEightyやalsoTenEightyの中の値を書き換えちゃっていいの?」となるが、tenEightyやalsoTenEightyはインスタンスそのものをしまっているのではなく、インスタンスを指している(参照している)だけなので、別にその先の値を書き換えても指しているインスタンスが変わったとかそういう訳ではないので問題ないのである。

というわけで、インスタンス同士を比較する(同一人物かどうかチェックする)ときは===を使おう。==は怒られる。

if tenEighty === alsoTenEighty {
	println("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}

なお、
===(!==)はidentical toであるか、つまり同一人物であるか(でないか)をチェックし、
==はequal toであるか、つまりそっくりさんであるかをチェックするものである。

で、どっちを使えばいいの

構造体をつかうとき:
互いに関係のあるデータをまとめて扱いたいとき
値型の性質を使いたいとき(コピーとかそういう話)
中にしまったデータもやっぱり値型で、値型らしい性質を使いたいとき
継承する用事がない時

と、マニュアルに書いてあるように思えるが、日本語訳に自信はなかった。
例えばwidthとheightというDouble型を一緒に扱うとか、startとlengthというInt型を一緒に扱うとか、xとyとzというDouble型をセットで、とか

コレクションの挙動

一応値型か参照型かと言われると値型ではあるらしいのだが……
正確には、Directoryは確かに値型で、Arrayはなんか中間っぽいふるまいをする。

Directoryは値型であり、上で紹介したようなのと同じ動作をする。つまり、別の変数や定数に代入したあと片方の値を変えても、もう片方には影響がない。

//キーがString、値がInt型
var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
var copiedAges = ages
copiedAges["Peter"] = 24	//agesには影響がない

//23
println(ages["Peter"])

一方、Arrayの方は特定の処理をしないかぎり参照型の動作になる。

var a = [1, 2, 3]
var b = a
var c = a

//もちろんすべて1
println(a[0])
println(b[0])
println(c[0])

a[0] = 42
//参照型のふるまいをするためa~c全てで0番目の要素が42に変わっている
println(a[0])
println(b[0])
println(c[0])

で、肝心の特定の処理なのだが、配列の長さを変えるような処理(追加や削除)や、unshare()のことである。

a.append(4)	//配列の長さが変わると同時にaはb・cとは別物になる
a[0] = 777	//この代入はbとcにはもはや影響しない

//777
println(a[0])
//42
println(b[0])
//42
println(c[0])

unshare()をすれば別に長さをわざわざ変えなくてもその配列はユニークとなる。ただし定数にunshare()は使えないらしい。

b.unshare()	//bもcとは別のものになった
b[0] = -105	//bだけが-105にかわる

//相変わらず777
println(a[0])
//自分だけ-105
println(b[0])
//そのまま42
println(c[0])

同じ配列を参照しているかどうかは===で調べられる。

if b[0...1] === b[0...1] {
	println("These two subarrays share the same elements.")
} else {
	println("These two subarrays do not share the same elements.")
}

この配列と同じ要素の配列を作りたい……けど、同じ配列を参照してほしくない場合はcopy()を使う。(1つの変数からしか参照されていない配列にしたいだけならunshare()で)

var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"]
var copiedNames = names.copy()

copiedNames[0] = "Mo"	//namesとは別の配列なのでnames[0]には影響がない
//Mohsen
println(names[0])

↑ PAGE TOP