• Введение:

Ход программы зависит от значений различных переменных, которые мы используем в нашем коде. В зависимости от значений переменных в нашей программе мы перемещаемся по потоку выполнения по мере необходимости.

Например, если мы используем какие-либо типы коллекций, трудно выполнять логику, когда коллекция модифицируется или изменяется. т.е. когда добавляются, удаляются или изменяются новые элементы. Мы все еще можем управлять такими сценариями разными способами.

Один из них использует Центр уведомлений, чтобы уведомлять нас всякий раз, когда происходит изменение значения свойства. Но если вы хотите проверить многие свойства в нашем коде, этот способ (с использованием Центра уведомлений) приведет нас к тяжелому фрагменту кода, чтобы вызвать все те свойства, которые нам нужно проверить. Другой способ, который лучше. В таких сценариях (также Apple часто использует это в своих библиотеках), известен как KVO (наблюдение за ключевыми значениями), который также напрямую связан с другим мощным механизмом под названием KVC (кодирование ключевого значения).

Примечание. Любое свойство, которое мы хотим отслеживать на предмет изменений, должно быть предметом жалобы KeyValueCoding (KVC).

Оба этих KVO и KVC обеспечивают эффективный способ написания нашего кода. Теперь приступим к KVC, а затем к KVO.

  • KVC:

KVC - это форма кодирования, которая позволяет вам получать доступ к свойствам объекта косвенно, используя для доступа к ним строки вместо методов доступа к свойствам или вместо прямого доступа к переменным. Чтобы включить такой механизм, ваши классы должны соответствовать неформальному протоколу NSKeyValueCoding. (ИЛИ)

Кодирование "ключ-значение" - это механизм косвенного доступа к свойствам объекта с использованием строк для идентификации свойств, а не посредством вызова метода доступа или прямого доступа к ним через переменные экземпляра.

Пример: Профиль класса: NSObject {

var firstName: String

var lastName: String

var customProfile: Profile

}

Если мы хотим присвоить значения объявленным выше переменным в классе init () или где-нибудь в файле класса, обычно мы делаем следующее:

self.firstName = «Роберт»

self.lastName = «Старк»

С KVC; нам нравится следующее:

self.setValue: «Роберт» для ключа: «firstName» //self.setValue: Любой для ключа: ключ / KeyPath

self.setValue: «Stark» для ключа: «lastName»

Чтобы получить значения свойств KVC, мы используем следующее:

let robertLastName = self.value (forKey: «lastName»)

Приведенный выше способ работы KVC аналогичен быстрой работе со словарями. Верно?

Здесь, в KVC; Вместо того, чтобы напрямую присваивать значения свойствам или использовать (#If available) методы установки объектов, мы просто присваиваем значения в keys / keyPaths. Мы используем ключи и значения, этот метод называется Кодирование значения ключа (KVC).

Примечание. Существует протокол, известный как неформальный протокол NSKeyValueCoding, который является обязательным для работы с KVC. Наши классы должны быть подтверждены для этого протокола, чтобы использовать KVC и KVO. NSObject подтверждает этот протокол. Таким образом, каждый класс, определенный в структуре Foundation и наследуемый от NSObject, соответствует NSKeyValueCoding протокол.

  • Что такое ключ & KeyPath?

Ключ: просто «Ключ» определяет отдельное свойство, которое мы хотим установить или получить значение. Поэтому его имя должно совпадать с названием свойства.

Пример: self.setValue: «Stark» для ключа: «lastName»

KeyPath: KeyPath формируется с точечным синтаксисом путем следования подстрокам, поэтому это не одно слово / строка. Путь к ключу представляет все свойства объекта, который мешает достижению желаемого значения / свойства.

Ex:

var myProfile: Профиль

self.setValue: «Baratheon» для ключа: « myProfile. customProfile.lastName»

KVC:

  • Здесь мы собираемся создать приложение с одним представлением для работы с KVC и KVO. В этом проекте есть два примера проекта, но тот, который мы здесь изучаем, не связан с каким-либо пользовательским интерфейсом. Другой - обновляет UILable в представлении ViewController при изменении текста. Ссылка на проект будет доступна внизу, чтобы вы могли легко изучить ее сами.
  • Во-первых, быстро создайте приложение с одним представлением.
  • Чтобы использовать KVC, наши классы должны подтвердить протокол NSKeyValueCoding. Итак, подтвердив NSObject, мы можем выполнить этот шаг. UIViewController уже подтверждает этот NSObject, поэтому мы можем вызывать методы из этого протокола без какой-либо настройки.
  • В Xcode в своем проекте создайте новый быстрый файл с именем Children с NSObject в качестве базового класса (Children наследуется от NSObject). Затем в этом файле класса объявите два свойства с именами 'name' и 'age', поместив '@objc dynamic 'перед объявлением . Так зачем нам это делать ???

→ В Swift есть много ключевых слов / атрибутов для помощи в компиляции, спецификациях времени выполнения, контроле доступа и т. Д. Пример: @escaping, @available и т. Д. Аналогичным образом, он также определяет ряд модификаторов объявления, чтобы изменять объявления свойств / одноклассники. Например, помечая объявление класса ключевым словом ‘final’, мы сообщаем компилятору, что класс не может быть подклассом. Это позволяет компилятору сделать ряд оптимизаций для повышения производительности. ‘dynamic’ также является модификатором объявления, который мы используем в Swift.

Динамическая отправка - одна из замечательных функций в Objective-C. Это просто означает, что среда выполнения Objective-C решает во время выполнения, какую реализацию конкретного метода или функции необходимо вызвать. Например, если подкласс переопределяет метод своего суперкласса, динамическая отправка определяет, какая реализация метода должна быть вызвана, подкласса или родительского класса. Это очень мощная концепция.

Swift по возможности использует среду выполнения Swift. В результате можно сделать ряд оптимизаций. В то время как Objective-C полагается исключительно на динамическую отправку, Swift выбирает динамическую отправку только в том случае, если у него нет другого выбора. Если компилятор может во время компиляции определить, какую реализацию метода ему нужно выбрать, он выиграет несколько наносекунд, отказавшись от динамической отправки.

Среда выполнения Swift по возможности выбирает другие параметры, такие как статическая и виртуальная отправка, вместо динамической отправки. Это сделано для повышения производительности. Статическая и виртуальная диспетчеризация выполняются намного быстрее, чем динамическая диспетчеризация. Несмотря на то, что мы говорим о наносекундах, чистый результат может быть впечатляющим. Многие функции, к которым мы привыкли, возможны только благодаря динамической среде выполнения Objective-C, включая Core Data и Key-Value Observing.

Динамическая отправка

Применяя модификатор объявления «dynamic» к члену класса, вы сообщаете компилятору, что для доступа к этому члену следует использовать динамическую отправку.

При добавлении к объявлению префикса ключевого слова «dynamic» объявление неявно помечается атрибутом objc. Атрибут objc делает объявление доступным в Objective-C, что является требованием для его отправки средой выполнения Objective-C.

Модификатор объявления ‘dynamic’ может использоваться только для членов класса. Структуры и перечисления не поддерживают наследование, что означает, что среде выполнения не нужно выяснять, какую реализацию необходимо использовать.

Итак, чтобы использовать KVC и KVO быстро, для свойств, которые мы хотим наблюдать в KVO, нам нужно объявить их с помощью ключевого слова @objc dynamic.

  • Теперь у нас есть два свойства в нашем классе Children: Инициализировать / определить их в методе инициализатора.

  • Теперь в файле класса ViewController объявите три дочерних экземпляра, как показано ниже:

Сначала мы используем объект ‘child1’. В методе viewDidLoad () мы инициализируем объект child1, а затем присваиваем значения его свойствам.

Если вы распечатаете такие свойства объектов ‘child1’, как имя и возраст, вы получите в результате присвоенные значения.

Теперь мы будем использовать методы KVC, чтобы сделать то же самое.

В приведенном выше фрагменте кода в первых двух строках мы устанавливаем желаемые значения для обоих свойств с помощью метода setValue: forKey:. обратите внимание, что ключевые строки совпадают с именами свойств.

Далее выполняем прямо противоположную задачу. Мы извлекаем значения из свойств с помощью метода valueForKey: и присваиваем их двум локальным переменным. А затем мы распечатали значения в консоли. Результат такой же, как и предыдущий.

Примечание. Если мы дадим ключи, отличные от имен свойств, приложение выйдет из строя. При написании кода, совместимого с KVC, очень важно позаботиться о том, чтобы ключевые строки действительно совпадали с именами свойств, иначе приложение просто развалится. Это не тот случай, когда мы имеем дело непосредственно со свойствами, так как невозможно ошибиться в их названиях; в таком случае компилятор выдаст ошибку, которая побудит нас исправить ее.

Со всем вышесказанным нам удалось увидеть, как писать код в стиле KVC, а также как устанавливать и получать значения с помощью ключей. В следующей части мы завершим проект.

работа с путями ключей:

Теперь перейдите к классу Children и добавьте следующее свойство.

@objc dynamic var child: Дети?

в методе viewDidLoad. Теперь добавьте следующие строки, которые инициализируют связанные объекты и присваивают им начальные значения:

В приведенном выше фрагменте в первых двух строках мы только что инициализировали объект ‘child2’ и его свойство ‘child’. Затем мы устанавливаем значения для свойств name и age для child2. Для свойства child элемента child2 мы использовали путь ключа для установки значений. Наблюдайте внимательно. мы можем получить значения, чтобы проверить, было ли присвоение успешным: раскомментируйте операторы печати для этого.

Теперь посмотрим, что, если у ребенка этого ребенка тоже есть ребенок…

Если вы хотите проверить значения / результат, используйте операторы печати для приведенного выше фрагмента.

До сих пор мы научились писать код жалобы KVC, используя ключи и ключевые пути. Далее мы узнаем, как наблюдать за изменениями стоимости собственности:

KVO:

Здесь мы увидим, какие действия следует предпринять, чтобы отслеживать изменения свойств. Прежде всего, позвольте мне представить вам список шагов, необходимых для реализации KVO:

1. Класс, свойства которого вы хотите наблюдать, должен соответствовать KVO. Это означает:

  • Класс должен быть KVC-совместимым в соответствии с тем, что мы уже видели во введении и в предыдущем разделе.
  • Класс должен иметь возможность отправлять уведомления автоматически или вручную (подробнее об этом мы узнаем позже).

2. Класс, который будет использоваться для наблюдения за свойством другого класса, должен быть установлен как наблюдатель.

3. Следует использовать специальный метод с именем наблюдатьValue (forKeyPath keyPath: String ?, объекта: Any ?, изменить: [NSKeyValueChangeKey: Any] ?, context: UnsafeMutableRawPointer?). реализовано в наблюдающем классе.

Посмотрим все по порядку. Самая важная вещь, когда мы хотим наблюдать за изменениями свойства, - это заставить наш класс наблюдать за этими изменениями. Это делается более или менее аналогично случайным уведомлениям (NSNotifications), но с использованием другого метода. Это метод addObserver (‹# T ## наблюдатель: NSObject ## NSObject #›, forKeyPath: ‹# T ## String #›, параметры: ‹# T ## NSKeyValueObservingOptions #›, контекст: ‹# T ## UnsafeMutableRawPointer? #›)

Здесь мы наблюдаем изменения значений свойств name и age объекта child1. Итак, в методе viewWillAppear () добавьте наблюдателей для объекта child1.

Этот метод принимает следующие параметры:

  • addObserver: это наблюдающий класс, обычно объект self.
  • forKeyPath: я думаю, вы понимаете, для чего это нужно. Это строка, которую вы использовали в качестве ключа или пути ключа, и она соответствует свойству, которое вы хотите наблюдать. Обратите внимание, что вы указываете здесь либо один ключ, либо путь ключа.
  • options: массив значений NSKeyValueObservingOptions.
  • контекст: это указатель, который можно использовать в качестве уникального идентификатора для изменения свойства, которое мы наблюдаем. Обычно устанавливается значение nil или NULL. Позже мы узнаем об этом подробнее.

Теперь, когда мы сделали наш класс способным наблюдать за любыми изменениями в двух вышеупомянутых свойствах, мы должны реализовать метод detectValueForKeyPath: ofObject: change: context:. Его реализация является обязательной, и у нее есть один большой недостаток. Это факт, который вызывается при каждом изменении KVO, и если вы наблюдаете много свойств, вы должны написать много операторов if, чтобы предпринимать правильные действия для каждого свойства. Однако это можно легко упустить из виду, поскольку преимущества KVO превышают это ограничение.

Вышеупомянутый метод будет вызываться каждый раз при изменении значения свойств, добавляемых наблюдателю. Здесь с помощью параметра key-path мы напечатали новое и старое значения свойств name и age.

Чтобы проверить эту функциональность, подключите действие для UIButton к вашему viewController, и в этот метод действия добавьте следующие строки кода:

Супер! После того, как мы установили новое значение для свойств name и age объекта child1, мы получили уведомление и запрошенные нами сообщения для отображения были показаны в отладчике. Как видите, в словарь включены и предыдущее, и новое значение.

Из словаря change вы можете извлечь любое значение, которое хотите (при необходимости), но самое главное, что получать уведомления об изменениях свойств очень просто.

добавьте следующие строки кода в метод действия кнопки и запустите программу.

Если вы заметите результат, мы получим два уведомления об изменениях в свойстве age. Но это кажется сбивающим с толку, потому что, хотя мы знаем объект, которому принадлежит каждое уведомление, программно мы ничего не можем сделать, чтобы определить объект, отправивший уведомление. Итак, как нам с этим справиться и как мы можем программно быть на 100% уверены в отношении объекта, которому принадлежит измененное свойство?

Ответ на поставленный выше вопрос один: мы воспользуемся аргументом context в addObserver (‹# T ## Observer: NSObject # # NSObject # ›, forKeyPath:‹ # T ## String # ›, options:‹ # T ## NSKeyValueObservingOptions # ›, context:‹ # T ## UnsafeMutableRawPointer? # ›). Я уже упоминал ранее, что цель контекста - однозначно идентифицировать изменение объекта, поэтому это лучший инструмент, который есть в нашем распоряжении.

Здесь я определил два контекста для каждого child1 и child2:

Обратите внимание, что значение контекста для каждого наблюдаемого свойства должно быть глобальной переменной, потому что оно должно быть доступно как из методов addObserver…, так и из методов наблюденияValueForKeyPath….

Теперь измените код в методах наблюдателя child1 и child2, передав параметр context. И затем нам нужно изменить метод наблюдения, а также следующее: Таким образом мы можем идентифицировать измененное свойство с помощью переданного параметра контекста. Таким образом, нам удалось программно указать каждое измененное свойство.

Наконец, прежде чем мы дойдем до конца этой главы, также очень важно в какой-то момент удалить добавленных вами наблюдателей. Нет рецепта, где это делать. Например, во многих случаях было бы полезно сделать это в ObserverValueForKeyPath: ofObject: change: context: после того, как вы обработали полученное уведомление. В других случаях вы должны сделать это после увольнения контроллера представления. Как правило, решение, которое вы примете, зависит от структуры вашего приложения. В этом примере мы сделаем это в методе viewWillDissapear:. Вот оно: для child2 я удалил наблюдателя в приведенном выше фрагменте.

Автоматические и ручные уведомления

По умолчанию система отправляет уведомление каждый раз, когда свойство изменяется при использовании KVO. Это подходит в большинстве случаев, однако бывают случаи, когда мы хотим получать уведомление не после того, как произошло изменение, а после того, как в нескольких свойствах произошли изменения, или позже. К счастью, iOS SDK предоставляет нам несколько довольно удобных методов, которые дают нам контроль над уведомлениями, поэтому мы можем отправлять их вручную, когда это действительно необходимо. Прежде чем мы углубимся в подробности, позвольте мне просто сказать, что использование метода, который вы увидите ниже, не является обязательным. Напротив, вы можете реализовать его, если и когда это действительно необходимо.

Теперь перейдем к сути: для управления уведомлениями, отправляемыми при изменении свойств, необходимо реализовать метод класса automaticNotifiesObserverForKey:. Параметр, который он принимает, представляет собой строковое представление ключа свойства, для которого необходимо управлять уведомлением, и возвращает логическое значение. Если вы не хотите, чтобы уведомление отправлялось после изменения значения наблюдаемого свойства, метод должен возвращать false. В любом другом случае вы должны позволить iOS принимать решение об уведомлениях.

На практике предположим, что мы не хотим, чтобы уведомление публиковалось при изменении свойства name класса Children. Имея это в виду, вот реализация этого метода в классе Children.

В предложении else мы вызываем тот же метод, используя класс super, чтобы позволить iOS обрабатывать все ключи, которые мы здесь явно не добавили, и значение, которое мы возвращаем тот, который мы возвращаем в конце.

Если вы запустите приложение на этом этапе, вы обнаружите, что в отладчике не появляется сообщение об изменении имени. Конечно, это то, чего мы желаем, поэтому нам удалось достичь своей цели. Но действительно ли мы это сделали?

Как вы понимаете, возвращая false в приведенном выше методе для конкретного ключа, нам удалось только остановить отправку соответствующих уведомлений. И, конечно же, это не означает уведомления вручную, это означает отсутствие уведомлений вообще! Чтобы отправить уведомление, когда мы так решим, мы должны использовать два других метода. Это willChangeValueForKey: и didChangeValueForKey:. При их использовании сначала должен быть вызван willChangeValueForKey:, затем новое значение должно быть присвоено свойству, а didChangeValueForKey: должен быть вызван в конце. Чтобы протестировать ручные действия, добавьте эти строки в метод действия viewcontroller.

Если вы запустите приложение сейчас, в отладчике появится сообщение об изменении имени, а это значит, что мы успешно отправили уведомление вручную!

Фактически, уведомление отправляется после вызова метода didChangeValueForKey:, поэтому разместите этот метод там, где вы считаете подходящим для получения уведомления.

Именно этого мы и ожидали от приложения! Как видите, нам удалось контролировать точку отправки уведомлений, и все это с небольшими усилиями! Обратите внимание, что между методами willChangeValueForKey: и didChangeValueForKey: может быть назначено несколько значений свойств.

Методы willChangeValueForKey: и didChangeValueForKey: не являются обязательными для использования, как показано выше. Их также можно реализовать в классе Children (или в вашем собственном классе).

Нажмите здесь для проекта, описывающего вышеупомянутое руководство.

Использованная литература:

  1. Http://michael-brown.net/2017/swift-and-kvo-context-variables/
  2. Https://www.appcoda.com/understanding-key-value-observing-coding/
  3. Https://cocoacasts.com/key-value-observing-kvo-and-swift-3
  4. Https://www.ralfebert.de/ios-examples/swift/property-key-value-observer/
  5. Https://blog.scottlogic.com/2015/02/11/swift-kvo-alternatives.html

Если вам нравятся мои руководства, подпишитесь на меня на medium. Вы можете связаться со мной через учетные записи my-twitter или connectedIn.

Спасибо за прочтение. Скоро поправится с другими уроками.

Увидимся!!!