@dynamicMemberLookup is een Swift-attribut dat het mogelijk maakt om toegang tot de eigenschappen van een object op runtime te overschrijven via subscript. Historisch gezien is dit mechanisme geïntroduceerd voor een meer transparante integratie met dynamische talen zoals Python, en is ook gericht op flexibele routing van gegevensaccess in proxy-objecten, dynamische modellen en DSL.
Probleem: Standaard Swift vereist dat eigenschappen expliciet worden verklaard en controleert hun bestaan op compileertijd. Soms is het echter nodig om toegang te krijgen tot eigenschappen op basis van hun naam, die mogelijk niet bestaat op compileertijd, bijvoorbeeld bij het werken met JSON, API's, proxy-objecten of scriptwraps.
Oplossing: Gebruik @dynamicMemberLookup en implementeer subscript(dynamicMember:) om pogingen om toegang te krijgen tot niet-bestaande eigenschappen af te vangen.
Voorbeeld 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
Belangrijkste kenmerken:
Kan dynamicMemberLookup ook voor klassen worden geïmplementeerd, of alleen voor structs?
Ja, het kan worden toegepast op klassen, structs en enums (vanaf Swift 5+). Het is belangrijk om de bijbehorende subscript te implementeren.
Wat gebeurt er als je toegang probeert te krijgen tot een niet-bestaande eigenschap via dynamicMemberLookup?
Het retourneert een waarde vanuit de subscript, de verantwoordelijkheid voor het afhandelen van een ontbrekende waarde ligt bij jouw implementatie.
Bijvoorbeeld, in het bovenstaande voorbeeld, als je toegang probeert te krijgen tot user.secret, wordt nil geretourneerd.
Is het mogelijk om dynamische toegang te krijgen met sleutels van een ander type, bijvoorbeeld Int?
Ja! Je kunt subscript(dynamicMember:) declareren met andere argumentlabels en combineren met reguliere subscripts.
@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] } }
Een ontwikkelaar gebruikt @dynamicMemberLookup voor een gewone gebruikersmodel (User), om toegang te krijgen tot alle velden via strings. De code wordt ondoorzichtig, de IDE heeft zijn hints verloren.
Voordelen:
Nadelen:
@dynamicMemberLookup is toegepast voor het werken met een willekeurig JSON-object, waarvan de velden van tevoren onbekend zijn. Het mechanisme maakt het mogelijk om elegant en veilig (via Any?/optionele binding) met ongestructureerde gegevens te werken.
Voordelen:
Nadelen: