Swift Academy · leksjon

@dynamicMemberLookup

Bygg dynamisk, typesikker tilgang til medlemmer.

Leksjon 4 av 413 trinn

@dynamicMemberLookup er en gratis leksjon i Swift Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Swift Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Swift Academy inneholder totalt 4 leksjoner.

Hva er dynamic member lookup?

Attributtet @dynamicMemberLookup lar en type svare på medlemsaksess med punktumsyntaks selv om medlemmet ikke er deklarert som en faktisk egenskap. Når De skriver value.something, sender Swift det videre til et spesielt subscript som De definerer.

Dette gir wrapper-typer et naturlig uttrykk, samtidig som all aksess håndteres på ett sted.

@dynamicMemberLookup
struct Settings {
    private var storage: [String: String] = ["theme": "dark"]
    subscript(dynamicMember key: String) -> String? {
        storage[key]
    }
}

let s = Settings()
print(s.theme ?? "none")

Det nødvendige subscriptet

En type som er merket med @dynamicMemberLookup, må implementere subscript(dynamicMember:). Når parameteren er en String, blir medlemsnavnet De skriver, til denne strengen ved kjøring.

Dermed kaller config.timeout subscriptet med nøkkelen "timeout".

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? 0
    }
}

let config = Config(values: ["timeout": 30, "retries": 3])
print(config.timeout)
print(config.retries)

Strengbasert oppslag er ikke typesikkert

Strengbasert dynamisk medlemsoppslag er fleksibelt, men mister kontrollen ved kompilering. Hvis De skriver et medlemsnavn feil, kompilerer koden fortsatt, og De får det subscriptet returnerer for en manglende nøkkel.

Nedenfor returnerer et ukjent medlem stille og rolig standardverdien i stedet for at byggingen mislykkes.

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? -1
    }
}

let config = Config(values: ["timeout": 30])
print(config.tmout)

Typesikkert oppslag med key paths

Av hensyn til sikkerheten kan De la subscriptet ta en KeyPath i stedet for en streng. Da kontrollerer kompilatoren at medlemmet finnes på den innkapslede typen, slik at full kontroll gjenopprettes samtidig som aksessen fortsatt videresendes.

Dette mønsteret passer godt for wrapper-typer som inneholder en sterkt typet verdi.

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct Boxed {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

let boxed = Boxed(user: User(name: "Ada", age: 36))
print(boxed.name)
print(boxed.age)

Hvorfor key path-versjonen er sikrere

Fordi subscriptet tar en KeyPath<User, T>, er bare medlemmer som faktisk finnes på User, tillatt. En skrivefeil som boxed.naem ville nå føre til en kompileringsfeil, i motsetning til strengversjonen.

De får bekvemmeligheten ved punktumsyntaks uten noen av risikoene.

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct ReadOnly {
    let value: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        value[keyPath: path]
    }
}

let r = ReadOnly(value: User(name: "Lin", age: 22))
print(r.name, r.age)

Skrivbare dynamiske medlemmer

Hvis subscriptet tilbyr en setter og tar en WritableKeyPath, kan De også tilordne verdier til videresendte medlemmer. Wrapper-en videresender skrivingen direkte til den innkapslede verdien.

Dette gir en transparent, muterbar wrapper.

struct Profile { var name: String; var score: Int }

@dynamicMemberLookup
struct Wrapper {
    var value: Profile
    subscript<T>(dynamicMember path: WritableKeyPath<Profile, T>) -> T {
        get { value[keyPath: path] }
        set { value[keyPath: path] = newValue }
    }
}

var w = Wrapper(value: Profile(name: "Mo", score: 0))
w.score = 99
print(w.value.score)

Videresende til et innkapslet objekt

Dynamisk medlemsoppslag passer godt for lette proxyer som stort sett videresender til en indre verdi, men legger til litt funksjonalitet. Subscriptet samler videresendingslogikken på ett sted.

Her videresender en loggingswrapper lesinger og skriver ut hvilket medlem som ble aksessert.

struct Account { let id: Int; let balance: Double }

@dynamicMemberLookup
struct Logged {
    let account: Account
    subscript<T>(dynamicMember path: KeyPath<Account, T>) -> T {
        let result = account[keyPath: path]
        print("accessed a member")
        return result
    }
}

let logged = Logged(account: Account(id: 7, balance: 100))
print(logged.balance)

Kombinere med ideer fra dynamic callable

Dynamisk medlemsoppslag fungerer godt sammen med generiske typer. Ett enkelt generisk subscript kan videresende egenskaper av alle typer, slik at én wrapper kan brukes med verdier av mange former.

Her fungerer den samme Proxy-en enten medlemmet er en String eller en Int.

struct Item { let title: String; let count: Int }

@dynamicMemberLookup
struct Proxy<Wrapped> {
    let wrapped: Wrapped
    subscript<T>(dynamicMember path: KeyPath<Wrapped, T>) -> T {
        wrapped[keyPath: path]
    }
}

let proxy = Proxy(wrapped: Item(title: "Book", count: 4))
print(proxy.title, proxy.count)

Strengbasert oppslag for JSON-lignende data

Strengvarianten har fortsatt en plass: Den kan representere løst strukturerte data, for eksempel analysert JSON, der medlemmene ikke er kjent ved kompilering. Hver aksess blir et ordbokoppslag.

Her eksponerer en liten JSON-lignende verdi feltene sine gjennom punktumsyntaks.

@dynamicMemberLookup
struct JSON {
    let fields: [String: String]
    subscript(dynamicMember key: String) -> String {
        fields[key] ?? ""
    }
}

let json = JSON(fields: ["city": "Oslo", "country": "Norway"])
print(json.city)
print(json.country)

Begrensninger De bør kjenne til

Dynamisk medlemsoppslag påvirker bare syntaksen for medlemsaksess; det legger ikke til ekte lagrede egenskaper. Verktøy som autofullføring fungerer for key path-varianten, men ikke for vilkårlige strengnøkler.

Foretrekk key path-varianten når den underliggende strukturen er kjent, og reserver strengvarianten for data som faktisk er dynamiske.

struct Point { let x: Int; let y: Int }

@dynamicMemberLookup
struct Safe {
    let point: Point
    subscript<T>(dynamicMember path: KeyPath<Point, T>) -> T {
        point[keyPath: path]
    }
}

let safe = Safe(point: Point(x: 3, y: 4))
print(safe.x + safe.y)

En praktisk dekorator

Her er en gjennomført wrapper som videresender alle egenskapslesinger til den indre verdien samtidig som den teller aksesser. Ekte rammeverk bruker denne ideen til observasjon og sporing.

Punktumsyntaksen på wrapper-en føles identisk med å bruke verdien direkte.

struct Sensor { let temperature: Double; let humidity: Double }

@dynamicMemberLookup
class Tracked {
    let sensor: Sensor
    var reads = 0
    init(_ s: Sensor) { sensor = s }
    subscript<T>(dynamicMember path: KeyPath<Sensor, T>) -> T {
        reads += 1
        return sensor[keyPath: path]
    }
}

let t = Tracked(Sensor(temperature: 21.5, humidity: 0.4))
print(t.temperature)
print(t.humidity)
print(t.reads)

Hurtigsjekk: typesikkert dynamisk oppslag

Husk hvilken subscript-parameter som gjør dynamisk medlemsoppslag typesikkert.

Oppsummering: Dynamisk medlemssøk

De har lært hvordan @dynamicMemberLookup fungerer:

  • Attributtet videresender tilgang med punktnotasjon som ikke er deklarert, til subscript(dynamicMember:).
  • En String-parameter gir fleksibel, men ukontrollert tilgang, noe som passer godt for JSON-lignende data.
  • En KeyPath-parameter gir full typesikkerhet og autoutfylling.
  • En WritableKeyPath med en setter lar Dem videresende skriveoperasjoner gjennom en wrapper.
  • Dette er en syntaksforenkling som passer naturlig sammen med proxy- og decorator-mønstre.

De har nå hele verktøykassen for key paths: lesing, skriving, bruk i samlinger og dynamisk videresending.

struct User { let name: String }

@dynamicMemberLookup
struct Wrap {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

print(Wrap(user: User(name: "Final")).name)
Gratis å komme i gang

Lær deg Swift med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
122
Leksjoner
409

Ofte stilte spørsmål

Er leksjonen «@dynamicMemberLookup» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Swift Academy, inkludert «@dynamicMemberLookup», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Swift Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «@dynamicMemberLookup»?

Bygg dynamisk, typesikker tilgang til medlemmer. Du øver på Swift Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Swift Academy?

Ingen tidligere erfaring er nødvendig. Swift Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «@dynamicMemberLookup»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Swift Academy-leksjonen?

Ja. Alle Swift Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Grunnleggende om KeyPath
  2. Bruke KeyPaths i samlinger
  3. Skrivbare og refererende KeyPaths
  4. @dynamicMemberLookup
← Tilbake til Swift Academy