@dynamicMemberLookup to adnotacja Swift, która pozwala na nadpisywanie dostępu do właściwości obiektu w czasie wykonywania za pomocą subscript. Historycznie ten mechanizm został wprowadzony dla bardziej przejrzystej integracji z dynamicznymi językami, takimi jak Python, oraz na celu bardziej elastycznego routowania dostępu do danych w obiektach proxy, dynamicznych modelach i DSL.
Problem: Standardowy Swift wymaga jawnego deklarowania właściwości i sprawdza ich istnienie w czasie kompilacji. Czasami jednak potrzebne jest odwoływanie się do właściwości po ich nazwie, która nie istnieje w czasie kompilacji, na przykład podczas pracy z JSON, API, obiektami proxy, opakowaniami skryptowymi.
Rozwiązanie: Użyj @dynamicMemberLookup i zaimplementuj subscript(dynamicMember:) w celu przechwytywania prób dostępu do nieistniejących właściwości.
Przykład kodu:
@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
Kluczowe cechy:
Czy można zaimplementować dynamicMemberLookup dla klas, czy tylko dla struktur?
Tak, można stosować dla klas, struktur i enumów (od Swift 5+). Ważne jest tylko zaimplementowanie odpowiedniego subscriptu.
Co się stanie, gdy odwołam się do nieistniejącej właściwości przez dynamicMemberLookup?
Zwracana jest wartość z subscriptu, odpowiedzialność za obsługę braku wartości leży po stronie Twojej implementacji.
Na przykład, w powyższym przykładzie, jeśli odwołasz się do user.secret, zostanie zwrócone nil.
Czy można uzyskać dostęp do dynamicznych właściwości za pomocą kluczy innego typu, na przykład Int?
Tak! Można zadeklarować subscript(dynamicMember:) z innymi etykietami argumentów i łączyć je z normalnymi subscriptami.
@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] } }
Programista używa @dynamicMemberLookup dla zwykłego modelu użytkownika (User), aby uzyskać dostęp do jakichkolwiek pól poprzez ciągi. Kod staje się nieprzejrzysty, IDE straciło podpowiedzi.
Plusy:
Minusy:
@dynamicMemberLookup zastosowano do pracy z dowolnym obiektem JSON, których pola są z góry nieznane. Mechanizm pozwala na elegancką i bezpieczną (przez Any?/binding opcjonalny) pracę z danymi niestrukturalnymi.
Plusy:
Minusy: