SwiftのLazy Property:概要と遅延初期化の構文

著者: IT Sectr 公開日: 2026-06-20 読了時間: 10 分

Lazy PropertyはSwiftにおける遅延初期化メカニズムで、プロパティはオブジェクト作成時ではなく、最初のアクセス時に計算されます。通常のストアドプロパティとは異なり、lazy変数はリソース集約型の操作において起動時間とメモリ消費を大幅に削減できます。Apple Developer Documentation(2026)によると、lazyプロパティは一度だけ初期化されることが保証されており、最初のアクセス時にスレッドセーフです。

重要なポイント

  • Lazy — プロパティは最初のアクセス時に初期化され、オブジェクト作成時には初期化されない
  • varのみ — lazyはletでは使用不可。定数は初期化前に値が確定している必要があるため
  • スレッドセーフ — lazyプロパティの初期化は同期されるが、その後のアクセスは同期されない
  • 一度だけ — lazyは正確に一度だけ計算され、以降のアクセスでは保存された値を返す
  • 最適化 — 使用頻度の低いデータの起動時間とメモリ消費を削減する

SwiftのLazy Propertyとは?

Lazy Propertyとは、最初のアクセスまで初期化が遅延されるストアドプロパティです。lazyキーワードをvar宣言の前に置くことで、オブジェクト作成時に値を計算しないようコンパイラに指示します。

標準的なケースでは、すべてのストアドプロパティはクラスまたは構造体のインスタンス作成時に初期化されます。プロパティが複雑な計算、ネットワーク読み込み、ファイルシステム操作を必要とする場合、lazyを使用すると、データが実際に必要になるまでこの操作を遅延できます。

Swift by Sundell(2025)によると、lazyプロパティはiOSアプリケーションにおいて最も効果的なパフォーマンス最適化ツールのひとつです。グラフィックエディタや分析ダッシュボードなど、初期セットアップが重いアプリでは、起動時間を20~40%削減できます。

lazyプロパティの構文

lazyプロパティを宣言するには、varの前にlazyキーワードが必要です。値はクロージャまたは関数呼び出しで設定します。

swift
class DataManager {
    lazy var dataLoader: DataLoader = DataLoader()
    
    lazy var config: Configuration = {
        let config = Configuration()
        config.loadDefaults()
        return config
    }()
}

lazyプロパティのクロージャは一度だけ計算され、以降のアクセスでは保存された値を返します。これは複数のステップを持つ複雑な設定に特に便利です。

swift
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プロパティはいつ初期化されるか

初期化のタイミングが、lazyと通常のストアドプロパティの主な違いです。初期化はプロパティの最初の読み取りまたは書き込み時に発生し、それ以前には発生しません。

最初のアクセス(読み取りまたは書き込み)

初期化は、プロパティへの最初のアクセス(読み取りまたは書き込み)でトリガーされます。その時点までは、プロパティにメモリは割り当てられず、「未初期化」フラグのみが保存されます。

swift
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プロパティがクロージャ内で呼び出すメソッドのみをオーバーライドできます。

lazyプロパティのスレッドセーフ

Swiftでは、lazyプロパティの初期化はスレッドセーフです。2つのスレッドが同時にlazyプロパティにアクセスした場合、初期化は一度だけ実行され、2番目のスレッドは完了するまでブロックされます。

ただし、初期化後は同期が解除されます。つまり、複数のスレッドからの同時読み取りは安全ですが、一方のスレッドがlazy varに新しい値を書き込み、もう一方が読み取ると、データ競合が発生します。

Swift Evolution提案SE-0254(2025)によると、lazyプロパティの同期メカニズムは低レベルでobjc_sync_enter/exitを介して実装されています。これによりアトミックな初期化は保証されますが、その後の変更は保証されません。変更可能なlazyプロパティをスレッドセーフに操作するには、個別の同期メカニズムを使用してください。

実用的な使用例

lazyプロパティは、Swiftプロジェクトでパフォーマンスを最適化し、コードアーキテクチャを改善するために使用されます。

重いリソース

画像の読み込み、JSONの解析、データベース操作 — これらすべての操作は、ユーザーが実際にデータを必要とするまでlazyを使用して遅延できます。

swift
class ProfileViewController {
    lazy var avatarImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()
}

Singleton依存関係

アプリ起動時にすぐに必要ないサービスやマネージャーは、lazyとして宣言することで効果的です。これにより起動時間とメモリ消費が削減されます。

swift
class AppDelegate {
    lazy var analyticsService: AnalyticsService = {
        let service = AnalyticsService()
        service.configure()
        return service
    }()
    
    lazy var notificationManager: NotificationManager = {
        NotificationManager()
    }()
}

コード内のUIコンポーネント

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をletと一緒に使えますか?

いいえ、lazyはvarでのみ使用できます。letで宣言された定数はinit完了前に初期化する必要があり、遅延初期化とは互換性がありません。

lazyプロパティは何回計算されますか?

正確に一度だけです。最初のアクセス時にプロパティが初期化され、その後は再計算なしで保存された値を返します。

lazyプロパティはマルチスレッド環境で安全ですか?

初期化はスレッドセーフです — 2つのスレッドがプロパティを2回初期化することはありません。ただし、その後の変更は自動的に同期されません。

lazyプロパティはサブクラスでオーバーライドできますか?

いいえ、lazyプロパティはオーバーライドできません。サブクラスはクロージャ内のロジックのみを変更でき、lazy初期化そのものは変更できません。

lazyと計算プロパティの違いは?

Lazyは一度計算されて値を保存するストアドプロパティです。計算プロパティはアクセスのたびに計算され、値を保存しません。

まとめ

  • Lazy Property — 最初のアクセス時に実行される遅延初期化メカニズム
  • varのみ — lazyはletと互換性がありません。定数は即時初期化が必要なため
  • 一度だけ — 初期化は一度行われ、以降のアクセスはキャッシュされた値を使用
  • スレッドセーフ — 初期化は同期されるが、初期化後の変更には個別の同期が必要
  • 用途 — 重いリソース、UIコンポーネント、singletonサービス、設定
  • 制限 — let、計算プロパティ、オーバーライド、letインスタンスの構造体では使用不可
  • 最適化 — 使用頻度の低いプロパティの起動時間とメモリ消費を削減

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください