Swift Academy · Lektion

@dynamicMemberLookup

Bygg dynamisk, typsäker åtkomst till medlemmar.

Lektion 4 av 413 steg

@dynamicMemberLookup är en gratis lektion i Swift Academy på CoddyKit. Detta är lektion 4 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Swift Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Swift Academy innehåller totalt 4 lektioner.

Vad är dynamisk medlemsuppslagning?

Attributet @dynamicMemberLookup låter en typ reagera på medlemsåtkomst med punktnotation även när medlemmen inte är deklarerad som en riktig egenskap. När Ni skriver value.something dirigerar Swift det till ett särskilt subscript som Ni definierar.

Detta ger wrapper-typer ett naturligt gränssnitt samtidigt som åtkomsten hålls samlad på ett ställe.

@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 obligatoriska subscriptet

En typ som är markerad med @dynamicMemberLookup måste implementera subscript(dynamicMember:). När parametern är en String blir medlemsnamnet Ni skriver den strängen vid körning.

Det innebär att config.timeout anropar subscriptet med nyckeln "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)

Strängbaserad uppslagning är inte typsäker

Strängbaserad dynamisk medlemsuppslagning är flexibel men förlorar kontrollen vid kompilering. Om Ni stavar ett medlemsnamn fel kompileras koden ändå, och Ni får det som subscriptet returnerar för en saknad nyckel.

Nedan returnerar en okänd medlem tyst standardvärdet i stället för att bygget misslyckas.

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

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

Typsäker uppslagning med key paths

För ökad säkerhet kan Ni låta subscriptet ta en KeyPath i stället för en sträng. Då verifierar kompilatorn att medlemmen finns i den omslutna typen, vilket återställer full kontroll samtidigt som åtkomsten fortfarande vidarebefordras.

Detta mönster passar utmärkt för wrapper-typer som innehåller ett starkt typat värde.

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)

Varför key path-versionen är säkrare

Eftersom subscriptet tar en KeyPath<User, T> tillåts endast medlemmar som faktiskt finns i User. Ett stavfel som boxed.naem skulle nu ge ett kompileringsfel, till skillnad från strängversionen.

Ni får punktnotationens bekvämlighet utan någon av riskerna.

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)

Skrivbara dynamiska medlemmar

Om subscriptet tillhandahåller en setter och tar en WritableKeyPath kan Ni även tilldela värden till vidarebefordrade medlemmar. Wrappern skickar skrivningen direkt vidare till det omslutna värdet.

Detta skapar 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)

Vidarebefordra till ett omslutet objekt

Dynamisk medlemsuppslagning passar utmärkt för enkla proxyer som huvudsakligen vidarebefordrar till ett inre värde men lägger till lite beteende. Subscriptet samlar vidarebefordringslogiken på ett ställe.

Här vidarebefordrar en loggningswrapper läsningar och skriver ut vilken medlem som användes.

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)

Kombinera med idéer från dynamic callable

Dynamisk medlemsuppslagning fungerar bra tillsammans med generics. Ett enda generiskt subscript kan vidarebefordra egenskaper av vilken typ som helst, så en wrapper kan användas för värden med många olika strukturer.

Här fungerar samma Proxy oavsett om medlemmen är 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)

Strängbaserad uppslagning för JSON-liknande data

Strängformen har fortfarande sin plats: för att representera löst strukturerade data, till exempel tolkad JSON, där medlemmarna inte är kända vid kompilering. Varje åtkomst blir då en dictionary-uppslagning.

Här exponerar ett litet JSON-liknande värde sina fält med punktnotation.

@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)

Begränsningar att tänka på

Dynamisk medlemsuppslagning påverkar endast syntaxen för medlemsåtkomst; den lägger inte till riktiga lagrade egenskaper. Verktyg som autocompletion fungerar för key path-formen, men inte för godtyckliga strängnycklar.

Föredra key path-versionen när den underliggande strukturen är känd, och reservera strängversionen för verkligt dynamiska data.

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 decorator

När allt sätts ihop får Ni här en genomtänkt wrapper som vidarebefordrar varje egenskapsläsning till sitt inre värde samtidigt som den räknar åtkomsterna. Riktiga ramverk använder denna idé för observation och spårning.

Punktnotationen på wrappern känns identisk med att använda värdet direkt.

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)

Snabbkontroll: typsäker dynamisk uppslagning

Kom ihåg vilken subscriptparameter som gör dynamisk medlemsuppslagning typsäker.

Sammanfattning: Dynamisk medlemsuppslagning

Ni har lärt er hur @dynamicMemberLookup fungerar:

  • Attributet dirigerar åtkomst med punktnotation till subscript(dynamicMember:) när medlemmen inte har deklarerats.
  • En String-parameter ger flexibel men okontrollerad åtkomst, vilket passar utmärkt för JSON-liknande data.
  • En KeyPath-parameter återställer full typsäkerhet och autokomplettering.
  • En WritableKeyPath med en setter låter er vidarebefordra skrivningar genom en wrapper.
  • Det är en syntaxförenkling som passar naturligt ihop med proxy- och decorator-mönster.

Ni har nu hela verktygslådan för key paths: läsa, skriva, använda samlingar och vidarebefordra dynamiskt.

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 att börja

Lär dig Swift med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
122
Lektioner
409

Vanliga frågor

Är lektionen ”@dynamicMemberLookup” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Swift Academy, inklusive ”@dynamicMemberLookup”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Swift Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”@dynamicMemberLookup”?

Bygg dynamisk, typsäker åtkomst till medlemmar. Ni övar på Swift Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Swift Academy?

Du behöver inga förkunskaper. Utbildningen i Swift Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”@dynamicMemberLookup”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Swift Academy-lektionen?

Ja. Varje Swift Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Grunderna i KeyPath
  2. Använda KeyPaths i kollektioner
  3. Skrivbara och referensbaserade KeyPaths
  4. @dynamicMemberLookup
← Tillbaka till Swift Academy