Swiftでは型を定義する場合、次のように Value Types が好まれる傾向にあります。
- Swift standard library の多くの型が Value Types で実装されている
Swift 2.0 - struct: 87 enum: 8 class: 4
- Prefer structs over classes by Github Swift Style Guide
このエントリでは 「Referece Types と Value Types の違い」、「それぞれの型の使い分けについての私の考え」をまとめてみました。
Reference Types と Value Types の違い
=
の挙動
-
Reference Types
- インスタンスへの参照ができる
- インスタンス自体はコピーされない
-
Value Types
- インスタンスがコピーされる
Reference Type
class Point {
var x: Float = 0.0
var y: Float = 0.0
}
let p1 = Point()
let p2 = p1
p2.x = 5.0
p1.x // x: 5.0, y: 0.0
p2.x // x: 5.0, y: 0.0
-
p1
,p2
には 同じPoint
インスタンスの参照が保持される - 結果、
p2.x
への変更がp1.x
の値にも反映される
Value Type
struct Point {
var x: Float = 0.0
var y: Float = 0.0
}
let p1 = Point()
var p2 = p1
p2.x = 5.0
p1.x // x: 0.0, y: 0.0
p2.x // x: 5.0, y: 0.0
-
p1
,p2
は別々のPoint
インスタンス - 結果、
p2.x
への変更はp1.x
の値に反映されない
Mutability
-
Reference Types と Value Types で
let
の意味するところが異なる。
Reference Types
-
let
: 参照が不変
class Point {
var x: Float = 0.0
var y: Float = 0.0
}
let p = Point()
p = Point() // ERROR Cannot assign to value: 'p' is a 'let' constant
- インスタンスの値は変更可能
p.x = 5.0
Value Types
-
let
: インスタンス自体が不変。プロパティの値も変更不可。(プロパティがlet
,var
に関係ない)
struct Point {
var x: Float = 0.0
var y: Float = 0.0
}
let p = Point()
p.x = 10.0 // ERROR Cannot assign to property: 'p' is a 'let' constant
Reference Types は、let
で変数を定数化しても、参照先が変更できないだけで、参照先のインスタンスの値は変更できてしまう。
よって、Value Types の方がMutabilityの管理が容易。
Reference Types にて同様のことを実現しようとすると NSString
と NSMutableString
みたいな関係のクラスを自前で実装する必要がある。
ここまでのまとめ
=
の挙動
-
Value Types
- インスタンスが複製される
-
Reference Types
- インスタンスへの参照ができる
Mutability
-
Value Types
-
let
によりインスタンスの値(プロパティ含む)を不変にできる
-
-
Reference Types
-
let
でインスタンスへの参照は不変になるが、インスタンスの値は変更可能
-
使い分け: Reference Types / Value Types
通常、型はmutabilityの管理が容易な方が好ましい。
まず Value Types で要件を満たせないかを検討する。
Value Types
-
=
において、インスタンスが複製され、独立性が維持された方が良い場合
let initialPoint = Point(x: 0, y: 0)
var circle = Shape(center: initialPoint)
let square = Shape(center: initialPoint)
// `circle` と `square` は独立したインスタンス
// それぞれが `center` を管理している
circle.center.x = 2 // {x: 2, y: 0}
square.center // {x: 0, y: 0}
Reference Types
- Cocoaのクラスを使う場合
-
XXXViewController
,YYYTableViewCell
など
-
- インスタンスをMutableにして共有したい場合
class Account {
var balance = 0.0
}
class Person {
let account: Account
init(_ account: Account) {
self.account = account
}
}
let account = Account()
let husband = Person(account)
let wife = Person(account)
husband.accout.balance += 1000
wife.account.balance -= 500
account.balance // 500
まとめ
「Reference Types と Value Types の違い」、「使い分け: Reference Types / Value Types」についてまとめてみました。
Swift standard libraryが Value Types を多く用いているのは immutability を担保し易いからでしょう。
このエントリを書く上で色々なブログエントリを読んだのですが、
型を作る場合、まずはStruct
で実装できないか検討するのがSwift的に良いプラクティスなのかと思うようになりました。