iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら。
今回は、Properties に関して。
Properties
プロパティは、クラス、構造体、Enumeration に関連した値です。Swift では、下記で説明するようにいくつかの種類のプロパティがあります。この記事は、Stored Properties と Computed Properties について。
1. Stored Properties
Stored Properties は一番シンプルなプロパティです。クラスと構造体のインスタンスの中に保存されます。変数とコンスタント、両方とも使用可能です。デフォルト値を設定することもできますし、初期化の際に値を設定・修正することもできます。
1 2 3 4 5 6 7 8 | struct FixedLengthRange { var firstValue: Int let length: Int } var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) // the range represents integer values 0, 1, and 2 rangeOfThreeItems.firstValue = 6 // the range now represents integer values 6, 7, and 8 |
上記の構造体は、変数のプロパティとコンスタントのプロパティを持っています。length コンスタントプロパティは、初期化後、修正することができません。
この構造体は、変数として宣言されているので、変数プロパティを変更することができます。この例では、firstValue を変更しています。しかし、構造体がコンスタントの場合には、変更することができません。たとえ、その構造体のプロパティが firstValue のように変数として定義されていてもです。
1 2 3 4 | let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // this range represents integer values 0, 1, 2, and 3 rangeOfFourItems.firstValue = 6 // this will report an error, even thought firstValue is a variable property |
この挙動は、以前 みたように、構造体はバリュー、クラスはリファレンスという違いから生まれるものです。構造体は、バリューなので、一度コンスタントとして宣言されると変更することができません。クラスの場合には、クラスインスタンスの変数・コンスタントは、リファレンスを持っているだけなので、参照先の値を変更することができます。もちろん、参照先のプロパティは、コンスタントではなく、変数である必要があります。
1 2 3 4 5 6 7 8 | class FixedLengthRange { var firstValue: Int = 0 let length: Int = 0 } let rangeOfFourItems = FixedLengthRange() rangeOfFourItems.firstValue = 10 // can change even rangeOfFourItems is constant rangeOfFourItems.length = 10 // cannot change because length is constant |
2. Lazy Stored Properties
Lazy stored propeties というものがあります。これは、プロパティが初期値が使用される時まで作成されないというものです。初期値が外的要因に左右される場合や、初期化するのに時間がかかり実際に使用される時まで実行されることが望ましくない場合に便利です。Lazy stored peropeties を使用するには、@lazy
キーワードをプロパティの前に使用します。
1 2 3 4 5 | class DataManager { @lazy var importer = DataImporter() var data = String[]() // the DataManager class would provide data management functionality here } |
この DataManager クラスは、DataImporter クラスの importer プロパティを持ちます。DataImporter クラスは、データをインプットファイル等からロードし、コンテンツを処理して、クラスを初期化することが想定されます。つまり、DataImporter のインスタンスを作成するのに時間がかかります。そのため、DataManager を作成すると同時に、DataImporter クラスのプロパティを初期化することはあまり得策ではありません。そこで、実際にこのプロパティを使用する時まで、このプロパティを初期化するのを遅らせたい場合に、@lazy
を使用します。
3. Computed Properties
Stored Properties の他に、Computed Properties というものがあります。これは、クラスと構造体、さらに Enumeration でも使用できます。このプロパティは実際に値を保存しているのではなく、他のプロパティ等の値を計算して、その値を返すというプロパティです。Computed Properties はその都度計算されるので、変数として宣言されなければなりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | struct Point { var x = 0.0, y = 0.0 } struct Size { var width = 0.0, height = 0.0 } struct Rect { var origin = Point() var size = Size() var center: Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } |
3 つの構造体があります。この Rect 構造体の center プロパティが、Computed Properties です。この center は、origin プロパティと size プロパティの値を使用して、値を返してます。こういうプロパティを Computed Properties と呼ぶようです。
1 2 3 4 5 6 | var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) let initialSquareCenter = square.center square.center = Point(x: 15.0, y: 15.0) println("square.origin is now at (\(square.origin.x), \(square.origin.y))") // prints "square.origin is now at (10.0, 10.0) |
このように使用します。
4. Computed Properties のセッター/ゲッター
上記のコードの Computed Properties のセッターをもう少し簡単に書くことができます。新しい値への名前を設定しなかった場合、Swift は自動的に newValue という名前を用意してくれ、それを使用することができます。
1 2 3 4 | set { origin.x = newValue.x - (size.width / 2) origin.y = newValue.y - (size.height / 2) } |
Computed Properties において、ゲッターはもちろん必要ですが、セッターは必ず実装しなければならないというわけではありません。つまり、read-only の Computed Properties を作成できます。get
キーワードを削除し、下記の volume プロパティのように実装すれば OK です。
1 2 3 4 5 6 7 8 9 | struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { return width * height * depth } } let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // prints "the volume of fourByFiveByTwo is 40.0 |