Swiftでlazyの使い方を復習してみる
こんにちは、iOSエンジニアのTanです。
普段はLazyを意識せずに実装してきましたが、さまざまなライブラリのソースコードを拝見したところ、lazyが使われている場所が多く、改めて復習してみたいと思います。
Swiftのドキュメントはlazyがこんな感じで定義されてます。
A lazy stored property is a property whose initial value is not calculated until the first time it is used
使われるまでに、初期valueが計算されない意味ですね。
lazyをつけたPropertyは下記のような特徴があります。
・初期値がインスタンス生成後の状態(他のプロパティなど)に依存している
・セットアップが複雑で重く、使われるまでは生成する必要がない
さて、実際のコードをみてみましょう。
struct Person {
let name: String
let age: Int
}
struct PeopleViewModel {
let people: [Person]
lazy var oldest: Person? = {
print("Lazy var oldest initialized")
return people.max(by: { $0.age < $1.age })
}()
init(people: [Person]) {
self.people = people
print("View model initialized")
}
}
PeopleViewModelを注目してください。oldestにlazyがつけられて、一回だけの計算で、peopleな中に一番お年寄りの人を取り出したいわけですね。
var viewModel = PeopleViewModel(people: [
Person(name: "Antoine", age: 30),
Person(name: "Jaap", age: 3),
Person(name: "Lady", age: 3),
Person(name: "Maaike", age: 27)
])
// Prints: "View model initialized"
print(viewModel.oldest)
// Prints: "Lazy var oldest initialized"
// Prints: Person(name: "Antoine", age: 30)
上記のコードをみると、oldestはviewModelのinstanceが生成された時にコールされなく、oldestがコールされたら実行されます。まさにセットアップが複雑で重く、使われるまでは生成する必要がないということですね。
続いて、初期値がインスタンス生成後の状態(他のプロパティなど)に依存しているをみていきたいです。
struct PeopleViewModel {
var people: [Person]
lazy var oldest: Person? = {
print("oldest person calculated")
return people.max(by: { $0.age < $1.age })
}()
}
var viewModel = PeopleViewModel(people: [
Person(name: "Antoine", age: 30),
Person(name: "Jaap", age: 3),
Person(name: "Lady", age: 3),
Person(name: "Maaike", age: 27)
])
print(viewModel.oldest)
// Prints: "oldest person calculated"
// Prints: Person(name: "Antoine", age: 30)
viewModel.people.append(Person(name: "Jan", age: 69))
print(viewModel.oldest)
// Prints: Person(name: "Antoine", age: 30)
最後の三行を注目してください。前のデータより年が高い人(Jan)を追加したのに、初期値に依存してしまっているため、printされたデータは前のvalueでした。
computed propertyとlazy stored propertyの違い
computed propertyの場合
struct PeopleViewModel {
let people: [Person]
var oldest: Person? {
print("oldest person calculated")
return people.max(by: { $0.age < $1.age })
}
}
print(viewModel.oldest)
// Prints: "oldest person calculated"
// Prints: Person(name: "Antoine", age: 30)
print(viewModel.oldest)
// Prints: "oldest person calculated"
// Prints: Person(name: "Antoine", age: 30)
lazy stored propertyの場合
struct PeopleViewModel {
let people: [Person]
lazy var oldest: Person? = {
print("oldest person calculated")
return people.max(by: { $0.age < $1.age })
}()
}
print(viewModel.oldest)
// Prints: "oldest person calculated"
// Prints: Person(name: "Antoine", age: 30)
print(viewModel.oldest)
// Prints: Person(name: "Antoine", age: 30)
ご覧のとおりcomputed propertyはコールされたたびに、毎回再計算になってしまいます。逆に、lazy stored propertyは一回だけコールされ、パフォーマンスはよくなります。
まとめ
lazy stored propertyの使い方少しだけわかってきたでしょうか?これからも実際のプロジェクトに取り組んでいきたいと思います。
この記事が気に入ったらサポートをしてみませんか?