Записываемые и ссылочные KeyPaths
Читайте и записывайте данные через ключевые пути.
«Записываемые и ссылочные KeyPaths» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Ключевые пути только для чтения и доступные для записи
Обычный KeyPath позволяет только читать значение. Чтобы записывать значение через ключевой путь, нужен доступный для записи вариант. В Swift есть два таких варианта: один для типов-значений, другой для ссылочных типов.
В этом уроке рассматриваются WritableKeyPath для структур и ReferenceWritableKeyPath для классов, а также то, как индексатор [keyPath:] становится доступным для записи.
struct Person {
var name: String
var age: Int
}
let writable: WritableKeyPath<Person, Int> = \Person.age
print(type(of: writable))WritableKeyPath для типов-значений
WritableKeyPath указывает на изменяемое хранимое свойство типа-значения. Чтобы записывать значение через такой путь, сам экземпляр должен быть объявлен как var, поскольку изменение свойства структуры изменяет всё значение.
Используйте индексатор [keyPath:] слева от присваивания, чтобы выполнить запись.
struct Person {
var name: String
var age: Int
}
var alice = Person(name: "Alice", age: 30)
let agePath = \Person.age
alice[keyPath: agePath] = 31
print(alice.age)Почему экземпляр должен быть переменной
Для типов-значений запись через ключевой путь изменяет копию на месте. Если экземпляр объявлен как let, изменять нечего, поэтому присваивание не скомпилируется.
Это правило соответствует обычному изменению свойства: установить хранимое свойство структуры можно только тогда, когда структура хранится в переменной.
struct Point {
var x: Int
var y: Int
}
var p = Point(x: 1, y: 2)
p[keyPath: \Point.x] = 10
p[keyPath: \Point.y] = 20
print(p)Обобщённый помощник для установки свойств
Поскольку доступный для записи ключевой путь определяет, какое свойство нужно изменить, можно написать обобщённую функцию, обновляющую любое свойство любого типа-значения. Передайте экземпляр как inout, чтобы изменение записалось обратно.
Это основа небольших средств для функционального обновления.
func update<Root, Value>(_ root: inout Root, _ path: WritableKeyPath<Root, Value>, to value: Value) {
root[keyPath: path] = value
}
struct Person { var name: String; var age: Int }
var bob = Person(name: "Bob", age: 40)
update(&bob, \.age, to: 41)
print(bob.age)ReferenceWritableKeyPath для классов
Для классов Swift предоставляет ReferenceWritableKeyPath. Поскольку экземпляр класса является ссылкой, запись через него возможна даже тогда, когда экземпляр хранится в константе let: изменяется не ссылка, а объект, на который она указывает.
В этом состоит ключевое отличие от доступных для записи ключевых путей для типов-значений.
class Counter {
var value: Int = 0
}
let counter = Counter()
let path: ReferenceWritableKeyPath<Counter, Int> = \Counter.value
counter[keyPath: path] = 5
print(counter.value)Неизменяемая ссылка, изменяемый объект
Обратите внимание: экземпляр класса ниже объявлен как let, но его свойство всё равно можно установить через ключевой путь. Со структурой это привело бы к ошибке компиляции.
Это происходит потому, что изменение свойства класса не требует изменяемой ссылки — изменяемым должно быть только само свойство.
class Profile {
var nickname: String = ""
}
let profile = Profile()
let nickPath = \Profile.nickname
profile[keyPath: nickPath] = "Ace"
print(profile.nickname)Иерархия типов ключевых путей
Типы доступных для записи ключевых путей образуют иерархию. ReferenceWritableKeyPath является подтипом WritableKeyPath, а тот, в свою очередь, — подтипом KeyPath. Поэтому более специализированный ключевой путь можно использовать везде, где ожидается более общий.
Это означает, что ReferenceWritableKeyPath можно передать функции, которой нужно только читать через KeyPath.
class Box { var size: Int = 1 }
func readSize(_ box: Box, _ path: KeyPath<Box, Int>) -> Int {
box[keyPath: path]
}
let writable: ReferenceWritableKeyPath<Box, Int> = \Box.size
print(readSize(Box(), writable))Обновление вложенных типов-значений
Доступные для записи ключевые пути можно объединять для вложенных структур. Установка глубоко вложенного свойства переписывает каждое содержащее его значение по всей цепочке — всё выполняется одним присваиванием.
Здесь мы изменяем город во вложенном адресе.
struct Address { var city: String }
struct Person { var name: String; var address: Address }
var carol = Person(name: "Carol", address: Address(city: "Oslo"))
carol[keyPath: \Person.address.city] = "Bergen"
print(carol.address.city)Применение нескольких изменений
Поскольку доступные для записи ключевые пути являются значениями, можно хранить список изменений в виде пар ключевого пути и значения, а затем применить его в цикле. Это небольшая система исправлений.
Здесь мы применяем два изменения к структуре настроек.
struct Settings { var volume: Int; var brightness: Int }
var settings = Settings(volume: 5, brightness: 5)
func apply(_ s: inout Settings, _ path: WritableKeyPath<Settings, Int>, _ v: Int) {
s[keyPath: path] = v
}
apply(&settings, \.volume, 8)
apply(&settings, \.brightness, 3)
print(settings)Настройка в стиле привязки
Распространённый практический вариант — помощник configure, который устанавливает свойство у только что созданного объекта и возвращает его, позволяя использовать цепочный стиль. С классами такой подход работает и для результата, объявленного как let.
Здесь мы устанавливаем текст у label через доступный для записи ключевой путь для ссылочного типа.
class Label { var text: String = "" }
func configure<T: AnyObject, V>(_ object: T, _ path: ReferenceWritableKeyPath<T, V>, _ value: V) -> T {
object[keyPath: path] = value
return object
}
let label = configure(Label(), \.text, "Hello")
print(label.text)Выбор подходящего типа ключевого пути
Используйте KeyPath, если нужно только читать, WritableKeyPath — чтобы устанавливать свойство типа-значения, хранящегося в var, и ReferenceWritableKeyPath — чтобы устанавливать свойство класса даже через let.
Выбор наиболее специализированного подходящего типа позволяет вашим программным интерфейсам честно отражать, читают они данные или изменяют их.
struct S { var n: Int }
class C { var n: Int = 0 }
let read: KeyPath<S, Int> = \S.n
let writeStruct: WritableKeyPath<S, Int> = \S.n
let writeClass: ReferenceWritableKeyPath<C, Int> = \C.n
print(type(of: read), type(of: writeStruct), type(of: writeClass))Быстрая проверка: доступные для записи ключевые пути
Вспомните, какой тип ключевого пути позволяет установить свойство класса, даже если экземпляр является константой let.
Итоги: доступные для записи и ссылочные ключевые пути
Вы научились записывать значения через ключевые пути:
WritableKeyPathустанавливает хранимое свойство типа-значения и требует, чтобы экземпляр был объявлен какvar.ReferenceWritableKeyPathустанавливает свойство класса и работает даже черезlet.- Индексатор
[keyPath:]появляется слева от присваивания, чтобы выполнить запись. - Типы образуют иерархию, поэтому доступный для записи ключевой путь можно использовать вместо пути только для чтения.
- На этой основе строятся обобщённые средства установки с
inoutи помощники configure.
Далее Вы увидите @dynamicMemberLookup, который использует ключевые пути для доступа к динамическим данным через точечный синтаксис.
struct Person { var name: String; var age: Int }
var dave = Person(name: "Dave", age: 27)
dave[keyPath: \Person.age] = 28
print("Updated age: \(dave.age)")Часто задаваемые вопросы
Урок «Записываемые и ссылочные KeyPaths» бесплатный?
Да — полный текст урока «Записываемые и ссылочные KeyPaths» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Записываемые и ссылочные KeyPaths»?
Читайте и записывайте данные через ключевые пути. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Записываемые и ссылочные KeyPaths»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы KeyPath
- Использование KeyPaths в коллекциях
- Записываемые и ссылочные KeyPaths
- @dynamicMemberLookup