Вступление:

Вокруг Property Wrappers было так много неразберихи. Почти все недавнее добавление (@State, @Published, @Binding, @BindableObject, @ObservedObject, @EnvironmentObject, @Environment) к платформе SwiftUI основано только на этой единственной функции, которая была представлена ​​в Swift 5.1 и называется «Property Wrappers».

О Property Wrappers уже написано так много статей, и некоторые из них просто великолепны. Я пишу эту статью не потому, что я тоже хотел написать об этом и присоединиться к победителям, а потому, что не все понимают тему, написанную определенным образом.

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

Кстати, у меня шоколад, а у тебя какой? ;)

Фон:

По собственным словам инженеров Apple, похоже, они осознали всю мощь этой функции только после того, как начали ее использовать, и было интересно посмотреть, как они начали с замены ленивого инициализированного вычисляемого свойства, а затем построили на нем другие слои. и то, что мы знаем сегодня как Property Wrappers (изначально предлагалось как Property Delegates, хотя и неплохая репутация).

Посмотрим, какую проблему он решит.

Я почти уверен, что как разработчики все мы определенно сталкивались с этой проблемой хотя бы раз в жизни.

Мы используем свойства в классах или структурах для хранения наших данных, и у нас есть требования обрабатывать данные, хранящиеся в свойствах, немедленно всякий раз, когда данные в свойстве изменяются / обрабатываются / модифицируются. Для этого существует множество примеров, и наиболее популярным из них является обертка свойств UserDefault в основном потому, что это простой вариант использования, который требуется / используется большинством приложений, и отчасти потому, что инженеры Apple продемонстрировали оболочку свойств в просто используя этот пример.

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

Пример:

В нашем примере представьте себе сценарий, допустим, у нас есть форма в приложении, которая не предназначена для использования детьми. Это означает, что нам нужно узнать возраст пользователя, чтобы проверить, является ли он / она взрослым.

Структура, представляющая объект / данные пользователя.

Свойство age, которое принимает значение типа Int.

Теперь наша цель - получать уведомления при каждом изменении свойства age, чтобы мы могли мгновенно проверить, является ли пользователь совершеннолетним или нет.

Для этого у нас уже есть несколько механизмов, таких как использование наблюдателей свойств ‘willSet’ / ’didSet’, но у них есть свои недостатки, такие как невозможность манипулировать / изменять значение свойства или отсутствие вызова во время установки начального значения соответственно. Хотя это не прямо / не применимо к нашему примеру, это просто примечание.

В нашем примере в идеале у нас будет расширение для Int или логическая переменная внутри структуры, которая будет содержать данные / состояние результата, полученного в результате проверки, которую мы выполняем для свойства 'age', идеальное решение с нашим существующие API будут выглядеть примерно так:

Но проблема с этим подходом заключается в том, что он не относится к этой проблеме, так как эта проверка не привязана к этой конкретной функции, или, другими словами, свойство isAdult доступно для всех типов Int, которые не нужны накладные расходы.

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

Введите Property Wrapper:

Чтобы создать оболочку свойств, все, что нам нужно сделать, это аннотировать структуру с помощью '@propertyWrapper', и все, что он просит, - это реализовать 'wrappedValue', поэтому давайте объявим нашу оболочку как «Взрослый», и вот как это будет выглядеть:

Value - это тип, который представляет Int в нашем случае, в идеале он может быть универсальным, чтобы можно было использовать любой тип, но давайте возьмем Value для нашего примера.

1.) Обратите внимание, что у нас есть defaultValue, которое требуется при работе с необязательной оболочкой, но есть способы иметь необязательную оболочку свойств, но мы проигнорируем ее в этой статье для простоты.

2.) isAdult - внутреннее свойство, содержащее результат нашей проверки.

3.) «wrappedValue», здесь все и происходит, все, что он должен иметь, это геттер и сеттер, если требуется, и обратите внимание, как мы выполняем проверку и сохраняем результат в «isAdult»

И, наконец, вот как мы можем использовать нашу оболочку свойств для свойства age в структуре User:

  1. @Adult - это наша оболочка свойств, и аннотирование свойства с ее помощью связывает его с нашей функцией обертывания и обратите внимание, как defaultValue передается в инициализаторе, который требуется при работе с необязательными
  2. Мы создаем объект User и печатаем свойство age
  3. Мы изменяем значение свойства age на возраст, не являющийся взрослым.
  4. Мы печатаем свойство isAdult, которое помещается внутри оболочки, и поэтому мы ставим перед ним знак подчеркивания, но мы не должны этого делать, я расскажу вам, почему
  5. Распечатываем саму обертку, в которой отображаются все свойства и их значения.
  6. Мы снова модифицируем значение свойства age до взрослого возраста.
  7. Мы печатаем значение, а затем саму оболочку, которая отображает все свойства и их значения.

projectedValue:

Теперь мы создали оболочку свойств и использовали ее, чтобы проверить, является ли пользователь совершеннолетним или нет. Одна вещь, которую мы сделали неправильно, - это прямой доступ к свойству ‘_age’, которое находится внутри оболочки, что не является идеальным способом сделать это. Чтобы избежать этого и преодолеть любые проблемы, которые могут возникнуть из-за этого, мы можем использовать свойство «projectedValue» оболочки, которое обычно возвращает саму оболочку, поэтому наша обновленная оболочка теперь должна выглядеть следующим образом:

и пользователи / клиенты / вызываемые могут ссылаться на него, добавляя префикс символа «$», и наша обновленная структура User выглядит так:

Просто сравните это с функцией привязки свойств SwiftUI, где мы используем префикс $ для привязки свойств между двумя представлениями для их синхронизации. Это та же функциональность, которую мы реализовали с помощью нашей собственной оболочки настраиваемых свойств.

Заключение:

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

Property-wrapper является относительно новым и имеет свои недостатки, как сказал Мэтт, но то, как инженеры Apple приняли его и широко использовали во фреймворке SwiftUI, заставляет нас всех следовать примеру, в конце концов, они первопроходцы.

Кроме того, есть несколько отличных ресурсов, связанных с этой замечательной темой - "Property Wrappers":

Https://nshipster.com/propertywrapper

Https://www.swiftbysundell.com/articles/property-wrappers-in-swift/

Https://www.avanderlee.com/swift/property-wrappers/

Https://github.com/SvenTiigi/ValidatedPropertyKit

Надеюсь, тебе понравилось!

Первоначально опубликовано на https://saru2020.blogspot.com 12 апреля 2020 г.