Lazy PropertyはSwiftにおける遅延初期化メカニズムで、プロパティはオブジェクト作成時ではなく、最初のアクセス時に計算されます。通常のストアドプロパティとは異なり、lazy変数はリソース集約型の操作において起動時間とメモリ消費を大幅に削減できます。Apple Developer Documentation(2026)によると、lazyプロパティは一度だけ初期化されることが保証されており、最初のアクセス時にスレッドセーフです。
重要なポイント
Lazy Propertyとは、最初のアクセスまで初期化が遅延されるストアドプロパティです。lazyキーワードをvar宣言の前に置くことで、オブジェクト作成時に値を計算しないようコンパイラに指示します。
標準的なケースでは、すべてのストアドプロパティはクラスまたは構造体のインスタンス作成時に初期化されます。プロパティが複雑な計算、ネットワーク読み込み、ファイルシステム操作を必要とする場合、lazyを使用すると、データが実際に必要になるまでこの操作を遅延できます。
Swift by Sundell(2025)によると、lazyプロパティはiOSアプリケーションにおいて最も効果的なパフォーマンス最適化ツールのひとつです。グラフィックエディタや分析ダッシュボードなど、初期セットアップが重いアプリでは、起動時間を20~40%削減できます。
lazyプロパティを宣言するには、varの前にlazyキーワードが必要です。値はクロージャまたは関数呼び出しで設定します。
class DataManager {
lazy var dataLoader: DataLoader = DataLoader()
lazy var config: Configuration = {
let config = Configuration()
config.loadDefaults()
return config
}()
}
lazyプロパティのクロージャは一度だけ計算され、以降のアクセスでは保存された値を返します。これは複数のステップを持つ複雑な設定に特に便利です。
class ImageCache {
lazy var cache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024
return cache
}()
}
複数のlazyプロパティは互いに独立して初期化されます。2つのlazyプロパティがクロージャを介して相互参照すると、循環依存が発生し、Swiftが自動的に解決できなくなります。
初期化のタイミングが、lazyと通常のストアドプロパティの主な違いです。初期化はプロパティの最初の読み取りまたは書き込み時に発生し、それ以前には発生しません。
初期化は、プロパティへの最初のアクセス(読み取りまたは書き込み)でトリガーされます。その時点までは、プロパティにメモリは割り当てられず、「未初期化」フラグのみが保存されます。
class ReportGenerator {
lazy var report: String = {
print("レポートを生成中...")
return "Annual report data"
}()
}
let generator = ReportGenerator()
// この時点ではレポートはまだ作成されていません
print(generator.report) // "レポートを生成中..."
print(generator.report) // 後続のアクセスでは再生成されません
lazyプロパティはサブクラスでオーバーライドできません。クラスがlazyプロパティを宣言した場合、子クラスはそれを異なる動作を持つ計算プロパティやストアドプロパティとしてオーバーライドできません。
Apple Swift Blog(2025)によると、この制限は、lazyがオーバーライド修飾子ではなく、ストレージ実装の詳細であるためです。サブクラスは、lazyプロパティがクロージャ内で呼び出すメソッドのみをオーバーライドできます。
Swiftでは、lazyプロパティの初期化はスレッドセーフです。2つのスレッドが同時にlazyプロパティにアクセスした場合、初期化は一度だけ実行され、2番目のスレッドは完了するまでブロックされます。
ただし、初期化後は同期が解除されます。つまり、複数のスレッドからの同時読み取りは安全ですが、一方のスレッドがlazy varに新しい値を書き込み、もう一方が読み取ると、データ競合が発生します。
Swift Evolution提案SE-0254(2025)によると、lazyプロパティの同期メカニズムは低レベルでobjc_sync_enter/exitを介して実装されています。これによりアトミックな初期化は保証されますが、その後の変更は保証されません。変更可能なlazyプロパティをスレッドセーフに操作するには、個別の同期メカニズムを使用してください。
lazyプロパティは、Swiftプロジェクトでパフォーマンスを最適化し、コードアーキテクチャを改善するために使用されます。
画像の読み込み、JSONの解析、データベース操作 — これらすべての操作は、ユーザーが実際にデータを必要とするまでlazyを使用して遅延できます。
class ProfileViewController {
lazy var avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
}
アプリ起動時にすぐに必要ないサービスやマネージャーは、lazyとして宣言することで効果的です。これにより起動時間とメモリ消費が削減されます。
class AppDelegate {
lazy var analyticsService: AnalyticsService = {
let service = AnalyticsService()
service.configure()
return service
}()
lazy var notificationManager: NotificationManager = {
NotificationManager()
}()
}
Storyboardを使わずにプログラムでインターフェースを構築する場合、lazyプロパティを使用すると、initを乱雑にせずにUI要素の初期化を整理できます。
1つ目の制限:lazyはletと併用できません。定数はinit完了前に初期化する必要があり、これは遅延初期化の考え方に反します。
2つ目の制限:lazyは計算プロパティでは使用できません。計算プロパティは値を保存しませんが、lazyは遅延初期化を行うストアドプロパティです。
3つ目の制限:構造体のlazyプロパティは変更に関する問題を引き起こす可能性があります。lazyプロパティを持つ構造体がletとして宣言されている場合、初期化には構造体の変更が必要なため、プロパティにアクセスできません。
4つ目:lazyプロパティは初期化中にwillSet/didSetをトリガーしません。プロパティがvarとして宣言されている場合、その後の変更でのみトリガーされます。
Stack Overflow(2025)によると、lazyに関する質問の約15%は、letコンテキストでのlazy使用の試みや、一度だけの初期化の誤解に関連しています。クラスを設計する際には、これらの制限を考慮することが重要です。
よくある質問
いいえ、lazyはvarでのみ使用できます。letで宣言された定数はinit完了前に初期化する必要があり、遅延初期化とは互換性がありません。
正確に一度だけです。最初のアクセス時にプロパティが初期化され、その後は再計算なしで保存された値を返します。
初期化はスレッドセーフです — 2つのスレッドがプロパティを2回初期化することはありません。ただし、その後の変更は自動的に同期されません。
いいえ、lazyプロパティはオーバーライドできません。サブクラスはクロージャ内のロジックのみを変更でき、lazy初期化そのものは変更できません。
Lazyは一度計算されて値を保存するストアドプロパティです。計算プロパティはアクセスのたびに計算され、値を保存しません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。