KeyPath, Swift'te bir nesnenin özelliğine türlenmiş bir yoldur ve birinci sınıf bir değer olarak temsil edilir. KVC'deki (Key-Value Coding) dize anahtarlarının aksine, KeyPath derleme zamanında derleyici tarafından kontrol edilir: derleyici kök nesnenin türünü, özelliğin türünü bilir ve yolun var olduğunu garanti edebilir. KeyPath, Combine'da reaktif bağlama için, SwiftUI'de iki yönlü bağlama için ve standart kütüphanede koleksiyonları sıralama ve filtreleme için kullanılır. Apple Developer, 2025'e göre KeyPath, Swift'te işlevsel programlama için temel bir yapı taşıdır.
Önemli Noktalar
KeyPath, bir nesnenin özelliğine giden yolu temsil eden Swift standart kütüphanesinden bir türdür. Nokta gösterimi (object.property) veya dize anahtarı (value(forKey:)) ile bir özelliğe manuel olarak erişmenin aksine, KeyPath birinci sınıf bir nesnedir: argüman olarak iletilebilir, bir değişkende saklanabilir ve birleştirilebilir.
Bir KeyPath, ters eğik çizgi ve özelliğe giden yol ile yazılır: \Person.name. Yol iç içe olabilir: \Person.address.city. Derleyici, Person'un name adlı bir özelliği olduğunu ve name'in gerçekten bir String olduğunu doğrular. Özellik yoksa veya tür yanlışsa, kod derlenmez.
struct Person: Codable {
let name: String
let age: Int
var isActive: Bool
}
// KeyPath (salt okunur)
let namePath: KeyPath<Person, String> = \Person.name
let person = Person(name: "Alice", age: 30, isActive: true)
let name = person[keyPath: namePath]
KeyPath aracılığıyla bir özelliğe erişim, object[keyPath: path] alt simgesi aracılığıyla yapılır. Salt okunur özellikler için KeyPath kullanılır. Özellik değiştirilebilir (var) ise, WritableKeyPath kullanılabilir. Başvuru türleri için, ReferenceWritableKeyPath, KeyPath aracılığıyla özelliği doğrudan değiştirmeye izin verir.
KeyPath, özelliğe erişim seviyesini yansıtan bir alt tür hiyerarşisine sahiptir. Temel hiyerarşi şöyledir: AnyKeyPath → PartialKeyPath → KeyPath → WritableKeyPath → ReferenceWritableKeyPath. Her alt tür yetenekler ekler.
KeyPath — salt okunur. WritableKeyPath — değer türleri (struct) için okuma ve yazma. ReferenceWritableKeyPath — başvuru türleri (class) için okuma ve yazma. Swift, bağlama göre otomatik olarak doğru alt türü seçer.
| Tür | Erişim | Nesne Türü | Değişiklik |
|---|---|---|---|
| KeyPath | Salt okunur | Herhangi | Hayır |
| WritableKeyPath | Okuma ve yazma | Değer türü (inout) | Mutating bağlamı aracılığıyla |
| ReferenceWritableKeyPath | Okuma ve yazma | Başvuru türü | Doğrudan atama |
// Değer türü için WritableKeyPath
var mutablePerson = Person(name: "Bob", age: 25, isActive: false)
let writablePath: WritableKeyPath<Person, Bool> = \Person.isActive
mutablePerson[keyPath: writablePath] = true
// Sınıf için ReferenceWritableKeyPath
class User: NSObject {
@objc dynamic var name: String = ""
}
let user = User()
let refPath: ReferenceWritableKeyPath<User, String> = \User.name
user[keyPath: refPath] = "Charlie"
Önemli bir fark: değer türleri için, KeyPath aracılığıyla ayarlama var (inout bağlamı) gerektirirken, başvuru türleri için yalnızca değiştirilebilir bir özellik yeterlidir. Bu, Swift'in genel anlambilimiyle uyumludur: değer türleri değer olarak iletilir, bu nedenle değişiklik kapsayıcının değiştirilebilirliğini gerektirir.
KeyPath, Combine ve SwiftUI'de merkezi bir rol oynar. Combine'da, assign operatörü bir yayıncının değerini bir nesnenin özelliğine bağlamak için KeyPath kullanır. SwiftUI'de, Binding, model ve görünüm arasında iki yönlü bağlantı için KeyPath kullanır.
assign(to:on:) operatörü bir ReferenceWritableKeyPath ve bir nesne kabul eder. Yayıncı bir değer yaydığında, otomatik olarak belirtilen özelliğe yazılır. Bu, manuel atamalar olmadan durumu yönetmenin bildirimsel bir yoludur.
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)
}
}
SwiftUI'de, @Binding bir üst ve alt görünümü bağlamak için KeyPath kullanır. Üst, Binding(get:set:) veya izdüşüm ($) aracılığıyla oluşturulan bir Binding
KeyPath, esnek ve tür güvenli sıralama ve filtreleme işlevleri yazmayı mümkün kılar. Her seferinde bir closure iletmek yerine, koleksiyonu sıralamak istediğiniz özelliğe bir KeyPath iletebilirsiniz. Bu, kodu daha temiz hale getirir ve tekrarı azaltır.
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)
Bu uygulama, closure yazmadan herhangi bir Comparable özelliğine göre sıralar. Soyadına göre sıralamak için, KeyPath'i değiştirmeniz yeterlidir: \Person.lastName. Özel bir anahtara göre sıralama gerekiyorsa (orneğin, ad uzunluğu), dahili olarak map kullanılır — geliştirici, standart olmayan durumlar için kendi closure'ını yazabilir.
AnyKeyPath, belirli kök ve değer türleri hakkındaki bilgileri silen KeyPath hiyerarşisindeki temel türdür. AnyKeyPath, hangi nesne türünde çağrıldığını veya hangi değer türünü döndürdüğünü bilmez. Bu, heterojen KeyPath'leri koleksiyonlarda saklamak ve yansıma için kullanışlıdır.
AnyKeyPath, farklı türlerdeki farklı özelliklere yolların bir listesini saklamanız gerektiğinde kullanılır. Örneğin, her yolun belirli bir özelliği yapılandırdığı ve özellik türlerinin farklılık gösterebildiği bir UI yapılandırıcısında. AnyKeyPath, tüm yollarla tek tip çalışmaya olanak tanır.
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 { }
// AnyKeyPath aracılığıyla özellikleri yapılandırmaya izin verir
AnyKeyPath, değer alınırken tür dönüşümü gerektirir, çünkü belirli tür silinmiştir. Tam tür güvenliği için, türlenmiş bir KeyPath
KeyPath, KVC dize anahtarlarından (value(forKey:)) önemli ölçüde daha verimlidir. Dize anahtarları, dize ayrıştırma, çalışma zamanı araması ve tür dönüşümü dahil olmak üzere Objective-C çalışma zamanı mekanizması aracılığıyla çözülür. KeyPath, yerel Swift yapılarına dayanan statik bir mekanizmadır.
Kıyaslamalara göre, KeyPath özelliklere erişim için value(forKey:)'den ortalama 10-20 kat daha hızlıdır. Fark, çalışma zamanı araması ve dinamik gönderimin olmamasından kaynaklanmaktadır. KeyPath, yeterli derleyici optimizasyonuyla bellektekai özellik ofsetine doğrudan erişir.
func readWithKeyPath(person: Person) -> String {
person[keyPath: \Person.name]
}
func readWithKVC(person: Person) -> String {
person.value(forKey: "name") as! String
}
KVC yalnızca daha yavaş olmakla kalmaz, aynı zamanda tür açısından güvensizdir: “name” dizesi mevcut olmayabilir ve as! String dönüşümü çökebilir. KeyPath, derleme zamanında özelliğin varlığını ve tür doğruluğunu garanti eder. Performans açısından kritik bölümler (animasyonlar, binlerce öğeli listeler) için KeyPath tek doğru seçimdir.
Sıkça Sorulan Sorular
KeyPath, normal bir değer olarak iletilebilen bir nesne özelliğine bir işaretçidir. person.name yazmak yerine, \Person.name yolunu oluşturur ve herhangi bir Person örneğinde özelliği okumak veya yazmak için kullanırsınız.
KeyPath — salt okunur. WritableKeyPath — inout bağlamı aracılığıyla değer türlerini (struct) okumak ve yazmak için. ReferenceWritableKeyPath — ek koşullar olmadan başvuru türlerini (class) okumak ve yazmak için.
KeyPath, Combine (assign(to:on:)), SwiftUI (Binding, FocusState), koleksiyon sıralama (sorted(by:)), Core Data (NSSortDescriptor) ve iç içe özelliklere güvenli erişim için yaygın olarak kullanılır.
Evet, KeyPath tamamen tür güvenlidir. Yoldaki bir hata (yazım hatası, yanlış tür) çalışma zamanında değil, derleme zamanında yakalanır. value(forKey:) dizeleri kullanır ve anahtar mevcut değilse NSUndefinedKeyException ile çökebilir.
Doğrudan hayır — Swift, bir dizeden dinamik KeyPath oluşturmayı desteklemez. Ancak, statik olarak elde edilen KeyPath'leri saklamak ve iletmek için AnyKeyPath'i kullanabilirsiniz. Dize ile dinamik özellik erişimi için, Objective-C çalışma zamanıyla value(forKey:)'i kullanın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun