@dynamicMemberLookup ist eine Swift-Annotation, die es ermöglicht, den Zugriff auf die Eigenschaften eines Objekts zur Laufzeit über subscript zu überschreiben. Historisch gesehen wurde dieser Mechanismus eingeführt, um eine transparentere Integration mit dynamischen Sprachen wie Python zu ermöglichen und zielt darauf ab, einen flexibleren Zugriff auf Daten in Proxy-Objekten, dynamischen Modellen und DSL zu ermöglichen.
Problem: Standard-Swift erfordert, dass Eigenschaften explizit deklariert werden und überprüft deren Existenz zur Kompilierzeit. Manchmal muss jedoch auf Eigenschaften nach ihrem Namen zugegriffen werden, der zur Kompilierzeit nicht existiert, z.B. bei der Arbeit mit JSON, API, Proxy-Objekten und Skripthüllungen.
Lösung: Verwenden Sie @dynamicMemberLookup und implementieren Sie subscript(dynamicMember:), um Zugriffsversuche auf nicht existierende Eigenschaften abzufangen.
Beispiel-Code:
@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
Schlüsselfunktionen:
Kann dynamicMemberLookup für Klassen oder nur für structs implementiert werden?
Ja, es kann für Klassen, structs und enums (seit Swift 5+) angewendet werden. Wichtig ist nur, das entsprechende subscript zu implementieren.
Was passiert, wenn man über dynamicMemberLookup auf eine nicht existierende Eigenschaft zugreift?
Es wird ein Wert aus subscript zurückgegeben, die Verantwortung für die Behandlung des Fehlens eines Wertes liegt bei Ihrer Implementierung.
Zum Beispiel, im obigen Beispiel, wenn man auf user.secret zugreift, wird nil zurückgegeben.
Kann man dynamischen Zugriff mit Schlüsseln eines anderen Typs, z.B. Int, machen?
Ja! Sie können subscript(dynamicMember:) mit anderen Argumentbezeichnungen deklarieren und mit normalen subscripts kombinieren.
@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] } }
Ein Entwickler verwendet @dynamicMemberLookup für ein normales Benutzermodell (User), um auf beliebige Felder über Strings zuzugreifen. Der Code wird undurchsichtig, die IDE hat ihre Hinweise verloren.
Vorteile:
Nachteile:
@dynamicMemberLookup wurde verwendet, um mit einem beliebigen JSON-Objekt zu arbeiten, dessen Felder im Voraus unbekannt sind. Der Mechanismus ermöglicht es, elegant und sicher (über Any?/optional binding) mit unstrukturierten Daten zu arbeiten.
Vorteile:
Nachteile: