lazy属性
変数の保持プロパティにlazyを宣言の直前につけると、インスタンス生成時ではなく、アクセス直前まで保持プロパティの右辺の式の評価を遅延させることができます。次の例をご覧ください
class SomeHeavyTask {
init() {
println("task generated")
}
}
class SomeTaskManager {
lazy var heavyTask = SomeHeavyTask()
}
var manager = SomeTaskManager()
manager.heavyTask // task generated
lazyを付けず、従来通りの宣言方法であれば、SomeTaskManagerインスタンスの生成時にSomeHeavyTaskインスタンスが生成され、値がheavyTaskプロパティにセットされます。しかし、lazyを付けた場合には、SomeHeavyTaskインスタンスの生成は、heavyTaskプロパティへの最初のアクセス時に行われます。
この例から見て取れるように、生成にコストのかかるクラスをプロパティに含み、インスタンス生成時には必要でない場合は、lazy属性を用いれば計算コストを削減できるでしょう。
計算プロパティ(Computed Property)
Objective-Cではプロパティに対する取得、代入の挙動をgetter/setterでカスタマイズできました。Swiftでも同様の機能が計算プロパティ(Computed Property)として定義されています。計算プロパティはクラス、ストラクチャ、列挙型のすべてに定義可能です。先ほどのCustomRangeStructクラスと類似したモデルを見てみましょう。
struct CustomRange {
var start: Int = 0
var length: UInt = 0
var end: Int {
get {
return start + Int(length)
}
set(newEnd) {
let newLength = newEnd - start
if newLength >= 0 {
length = UInt(newLength)
}
}
}
}
var range = CustomRange()
let end = range.end // 0
range.end = 2
let length = range.length // 2
こちらの例のように、プロパティ宣言の直後に、
{
get {
文
return 計算結果
}
set(新たな値) {
代入時の計算
}
}
という構文を入れることで、Objective-Cのgetter/setterに似たプロパティを宣言できます。動作もgetter/setterと同様で、外部内部問わず、取得あるいは代入されたときに呼び出されます。
setterの引数は省略が可能で、newValueというデフォルト名を使うことも可能です。先ほどのCustomRangeクラスのendプロパティの宣言部分を略記法で書き換えると、次のようになります。
var end: Int {
get {
return start + Int(length)
}
set {
let newLength = newValue - start
if newLength >= 0 {
length = UInt(newLength)
}
}
}
setterは必須ではないため省略可能です。また、その場合には「get」と括弧も省略可能です。今度は、getterのみの計算プロパティcenterを追加してみましょう。
struct CustomRange {
var start: Int = 0
var length: UInt = 0
var end: Int {
get {
return start + Int(length)
}
set(newEnd) {
let newLength = newEnd - start
if newLength >= 0 {
length = UInt(newLength)
}
}
}
var center: Double {
return Double(start + end) * 0.5
}
}
var range = CustomRange()
range.end = 2
let center = range.center // 1.0
willSet/didSetによるプロパティ監視
Objective-CでもKVO(Key-Value-Observing)という機構を用いれば、プロパティの値を監視することができました。Swiftではプロパティ値がsetされる直前あるいは直後のタイミングをwillSet/didSet構文で取得できます。
次の例では、lengthプロパティに値がsetされる直前に「length will change」、setされた直後に「length did change」と出力されます。
struct CustomRange {
var start: Int = 0
var length: UInt = 0 {
willSet {
if length != newValue {
println("length will change")
}
}
didSet (newLength) {
if length != newLength {
println("length did change")
}
}
}
}
var range = CustomRange()
range.length = 1
// length will change
// length did change
ただし、イニシャライザでのプロパティ呼び出しのタイミングは取得できませんので、注意してください。
