@dynamicMemberLookupは、Swiftのアノテーションで、オブジェクトのプロパティへのアクセスを実行時にsubscriptを通じてオーバーライドすることを可能にします。歴史的に、このメカニズムは、Pythonなどの動的言語との統合をより透明にするために導入され、プロキシオブジェクト、動的モデル、DSLにおけるデータアクセスの柔軟なルーティングを目指しています。
問題: 標準的なSwiftはプロパティを明示的に宣言し、コンパイル時にその存在を確認する必要があります。しかし、JSON、API、プロキシオブジェクト、スクリプトラッパーなどと作業する際など、コンパイル時に存在しないプロパティ名を使ってアクセスする必要がある場合があります。
解決策: @dynamicMemberLookupを使用し、存在しないプロパティへのアクセス試行をキャッチするためにsubscript(dynamicMember:)を実装します。
コードの例:
@dynamicMemberLookup struct JSON { private var data: [String: Any] subscript(dynamicMember member: String) -> Any? { data[member] } } let user = JSON(data: ["name": "Anna", "age": 23]) print(user.name as? String) // Anna
重要な特徴:
クラスに対してdynamicMemberLookupを実装できますか、それともstructのみに限られますか?
はい、クラス、struct、およびenum(Swift 5+から)に適用できます。適切なsubscriptを実装することが重要です。
dynamicMemberLookupを通じて存在しないプロパティにアクセスしたらどうなりますか?
subscriptからの値が返され、値が存在しない場合の処理の責任は実装側にあります。
たとえば、上の例でuser.secretにアクセスすると、nilが返されます。
異なる型のキー(例えばInt)を使って動的アクセスを行うことはできますか?
はい!他の引数ラベルを持つsubscript(dynamicMember:)を宣言し、通常のsubscriptと組み合わせることができます。
@dynamicMemberLookup struct ArrayProxy { private let array: [Int] subscript(dynamicMember member: String) -> Int? { if member == "first" { return array.first } if member == "last" { return array.last } return nil } subscript(index: Int) -> Int? { array[safe: index] } }
開発者が@dynamicMemberLookupを通常のユーザーモデル(User)に使用し、任意のフィールドに文字列を通じてアクセスします。コードが不透明になり、IDEは提案を失いました。
利点:
欠点:
@dynamicMemberLookupは、フィールドが事前に不明な任意のJSONオブジェクトで作業するために適用されます。このメカニズムは、非構造化データをエレガントかつ安全に(Any?/optional bindingを通じて)扱うことを可能にします。
利点:
欠点: