KeyPath — egy típusos útvonal egy objektum tulajdonságához a Swift-ben, első osztályú értékként reprezentálva. Ellentétben a KVC (Key-Value Coding) szöveges kulcsaival, a KeyPath-et a fordító ellenőrzi a fordítási fázisban: a fordító ismeri a gyökérobjektum típusát, a tulajdonság típusát, és garantálni tudja, hogy az útvonal létezik. A KeyPath a Combine-ban reaktív kötéshez, a SwiftUI-ban kétirányú bindeléshez és a szabványos könyvtárban kollekciók rendezéséhez és szűréséhez használatos. A Apple Developer, 2025 szerint a KeyPath alapvető építőköve a funkcionális programozásnak a Swift-ben.
Főbb pontok
KeyPath — egy típus a Swift szabványos könyvtárából, amely egy útvonalat reprezentál egy objektum tulajdonságához. Ellentétben a tulajdonság kézi elérésével ponton keresztül (object.property) vagy szöveges kulccsal (value(forKey:)), a KeyPath egy első osztályú objektum: átadható argumentumként, tárolható változóban és kombinálható.
A KeyPath fordított perjellel és a tulajdonsághoz vezető útvonallal íródik: \Person.name. Az útvonal lehet egymásba ágyazott: \Person.address.city. A fordító ellenőrzi, hogy a Person rendelkezik-e name tulajdonsággal, és hogy a name valóban String típusú. Ha a tulajdonság nem létezik vagy a típus helytelen, a kód nem fordítható le.
struct Person: Codable {
let name: String
let age: Int
var isActive: Bool
}
// KeyPath (csak olvasás)
let namePath: KeyPath<Person, String> = \Person.name
let person = Person(name: "Alice", age: 30, isActive: true)
let name = person[keyPath: namePath]
A tulajdonság elérése KeyPath-en keresztül a object[keyPath: path] subscript segítségével történik. ReadOnly tulajdonságokhoz KeyPath használatos. Ha a tulajdonság változtatható (var), a WritableKeyPath elérhető. Referenciatípusokhoz — ReferenceWritableKeyPath, amely lehetővé teszi a tulajdonság módosítását magán a KeyPath-en keresztül.
KeyPath altípusok hierarchiájával rendelkezik, amely tükrözi a tulajdonsághoz való hozzáférés szintjét. Az alaphierarchia: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Minden altípus új képességeket ad hozzá.
KeyPath — csak olvasás. WritableKeyPath — olvasás és írás értéktípusokhoz (struct). ReferenceWritableKeyPath — olvasás és írás referenciatípusokhoz (class). A Swift automatikusan kiválasztja a megfelelő altípust a kontextus alapján.
| Típus | Hozzáférés | Objektum típusa | Módosítás |
|---|---|---|---|
| KeyPath | Csak olvasás | Bármely | Nem |
| WritableKeyPath | Olvasás/írás | Értéktípus (inout) | Mutating kontextuson keresztül |
| ReferenceWritableKeyPath | Olvasás/írás | Referenciatípus | Közvetlen hozzárendelés |
// WritableKeyPath értéktípushoz
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true
// ReferenceWritableKeyPath osztályhoz
class User: NSObject {
@objc dynamic var name: String = ""
}
let user = User()
let refPath: ReferenceWritableKeyPath<User, String> = \User.name
user[keyPath: refPath] = "Charlie"
Fontos különbség: értéktípusoknál a KeyPath-en keresztüli beállításhoz var (inout kontextus) szükséges, referenciatípusoknál pedig változtatható tulajdonság. Ez megfelel a Swift általános szemantikájának: az értéktípusok érték szerint kerülnek átadásra, így a változtatás a konténer változtathatóságát igényli.
KeyPath központi szerepet játszik a Combine-ban és a SwiftUI-ban. A Combine-ban az assign operátor a KeyPath-et használja a kiadó értékének egy objektum tulajdonságához kötésére. A SwiftUI-ban a Binding a KeyPath-et használja a modell és a nézet kétirányú összekapcsolására.
Az assign(to:on:) operátor egy ReferenceWritableKeyPath-et és egy objektumot fogad. Amikor a kiadó értéket küld, az automatikusan a megadott tulajdonságba íródik. Ez egy deklaratív módja az állapot kezelésének kézi hozzárendelések nélkül.
import Combine
class SettingsViewModel: ObservableObject {
@Published var volume: Float = 0.5
var cancellables = Set<AnyCancellable>()
func bindSlider(publisher: AnyPublisher<Float, Never>) {
publisher
.assign(to: \SettingsViewModel.volume, on: self)
.store(in: &cancellables)
}
}
A SwiftUI-ban a @Binding a KeyPath-et használja a szülő és gyermek View összekapcsolására. A szülő egy Binding
KeyPath lehetővé teszi rugalmas és típusbiztos rendező- és szűrőfüggvények írását. Ahelyett, hogy minden alkalommal closure-t adna át, átadhat egy KeyPath-et arra a tulajdonságra, amely szerint rendezni kívánja a kollekciót. Ez tisztábbá teszi a kódot és csökkenti az ismétlődést.
A sorted
extension Sequence {
func sorted<Value: Comparable>(
by keyPath: KeyPath<Element, Value>,
ascending: Bool = true
) -> [Element] {
ascending
? self.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
: self.sorted { $0[keyPath: keyPath] > $1[keyPath: keyPath] }
}
}
let sortedByName = people.sorted(by: \Person.name)
let sortedByAgeDesc = people.sorted(by: \Person.age, ascending: false)
Egy ilyen implementáció bármely Comparable tulajdonság szerint rendez closure-ok írása nélkül. A vezetéknév szerinti rendezéshez elég a KeyPath-et megváltoztatni: \Person.lastName. Ha egyéni kulcs szerint kell rendezni (pl. név hossza), a map belül használatos — a fejlesztő továbbra is írhat saját closure-t nem szabványos esetekre.
AnyKeyPath — a KeyPath hierarchia alaptípusa, amely eltünteti a gyökér és az érték konkrét típusaira vonatkozó információkat. Az AnyKeyPath nem tudja, hogy milyen objektumtípuson hívják meg és milyen értéktípust ad vissza. Ez hasznos heterogén KeyPath-ek kollekciókban való tárolására és reflexióra.
Az AnyKeyPath akkor használatos, amikor különböző típusok különböző tulajdonságaihoz vezető útvonalak listáját kell tárolni. Például egy UI konfigurátorban, ahol minden útvonal egy adott tulajdonságot konfigurál, és a tulajdonságtípusok eltérőek lehetnek. Az AnyKeyPath egységes munkavégzést tesz lehetővé az összes útvonallal.
protocol Configurable {
func apply(_ keyPath: any PartialKeyPath<Self>, value: any)
}
extension Configurable {
func configure(_ pairs: (any KeyPath<Self, any>, any)...) {
for (path, value) in pairs {
apply(path, value: value)
}
}
}
extension Person: Configurable { }
// Lehetővé teszi tulajdonságok konfigurálását AnyKeyPath-en keresztül
Az AnyKeyPath típuskonverziót igényel az érték kinyerésekor, mivel a konkrét típus eltűnt. A teljes típusbiztonság érdekében mindig a típusos KeyPath
KeyPath jelentősen teljesítményesebb, mint a KVC (value(forKey:)) szöveges kulcsai. A szöveges kulcsok az Objective-C runtime mechanizmusán keresztül számolódnak ki, beleértve a szöveg elemzését, runtime keresést és típuskonverziót. A KeyPath egy statikus mechanizmus, amely a Swift natív struktúráin alapul.
Tesztek szerint a KeyPath átlagosan 10-20-szor gyorsabb, mint a value(forKey:) a tulajdonságok eléréséhez. A különbség a runtime keresés és a dinamikus dispatch hiányának köszönhető. A KeyPath közvetlenül a tulajdonság memória-eltolásához fér hozzá a fordító megfelelő optimalizálásával.
func readWithKeyPath(person: Person) -> String {
person[keyPath: \Person.name]
}
func readWithKVC(person: Person) -> String {
person.value(forKey: "name") as! String
}
A KVC nemcsak lassabb, hanem nem biztonságos a típusok szempontjából: a „name“ szöveg nem létezhet, az as! String konverzió pedig összeomolhat. A KeyPath garantálja a tulajdonság létezését és a típus helyességét a fordítási fázisban. A teljesítmény szempontjából kritikus részekhez (animációk, több ezer elemből álló lista) a KeyPath az egyetlen helyes választás.
Gyakran Ismételt Kérdések
KeyPath — egy mutató egy objektum tulajdonságára, amely átadható mint egy közönséges érték. Ahelyett, hogy person.name-t írna, létrehozza a \Person.name útvonalat, és azt használja a tulajdonság olvasására vagy írására bármely Person példányban.
KeyPath — csak olvasás. WritableKeyPath — értéktípusok (struct) olvasásához és írásához inout kontextuson keresztül. ReferenceWritableKeyPath — referenciatípusok (class) olvasásához és írásához további feltételek nélkül.
A KeyPath széles körben használatos a Combine-ban (assign(to:on:)), SwiftUI-ban (Binding, FocusState), kollekciók rendezésénél (sorted(by:)), Core Data-ban (NSSortDescriptor) és beágyazott tulajdonságok biztonságos eléréséhez.
Igen, a KeyPath teljesen típusbiztos. Az útvonalban lévő hiba (elírás, helytelen típus) a fordítási fázisban észlelhető, nem runtime-ban. A value(forKey:) szövegeket használ, és NSUndefinedKeyException-nel összeomolhat, ha a kulcs nem létezik.
Közvetlenül — nem, a Swift nem támogatja a KeyPath dinamikus létrehozását szövegből. Azonban használhatja az AnyKeyPath-et statikusan megszerzett KeyPath-ek tárolására és átadására. A tulajdonságok szöveg alapján történő dinamikus eléréséhez használja a value(forKey:)-et az Objective-C runtime-tal.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is