Понимание ключевого слова Final и расшифровка тайны.

Хорошо! Давайте приступим к делу! Для всех нас, разработчиков iOS, наступает момент, когда мы встречаем в коде Final. В то время мы спрашиваем себя, что он делает? Где это использовать? Как это использовать? Когда не использовать? И многие другие вопросы начинают появляться в наших головах.

Здесь я постараюсь ответить на эти вопросы и, надеюсь, раскрою тайну final к концу.

Последствия использования final

  1. Он предотвращает переопределение и наследование.
  2. Это увеличивает производительность кода во время выполнения.

Но что это значит??? Давайте посмотрим!

Предотвратить наследование и переопределение

Swift, как и большинство других объектно-ориентированных языков, позволяет классу наследовать от другого класса и переопределять методы и свойства, объявленные в его суперклассе.

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

В таких случаях объявление класса как final предотвратит его создание подкласса и, таким образом, предотвратит переопределение члена.

Точно так же бывают случаи, когда мы не хотим изменять реализации некоторых членов класса. В этом случае мы не хотим отменять эти несколько членов. Для этого нужно объявить ихfinal.

Примечание. Не обращайте внимания на обработку крайних регистров в функциях-членах класса User.

Повышение производительности во время выполнения

Экономия времени и отказ от вычислений компилятором во время выполнения делает программу эффективной и быстрой. Это то, что делает final более интересным и полезным для любого программиста вроде меня.

Swift, будучи языком ООП, позволяет определять класс и наследование. Это означает, что могут быть классы и подклассы, а подклассы могут / не могут переопределять свойства и методы базового класса.

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

  1. Проверьте, унаследован ли класс где-нибудь в коде или сам является подклассом.
  2. Проверьте, не переопределены ли какие-либо члены класса, и сколько раз. И какие возможные реализации доступны для указанного элемента member.
  3. Получите правильную реализацию из нескольких переопределений.
  4. Косвенно вызовите правильную реализацию из пула реализаций.

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

Вся эта цепочка событий косвенного доступа к указанному элементу называется в Apple динамической отправкой. Но обязательно ли постоянно проходить через Dynamic Dispatch - НЕТ! Тогда возникает естественный вопрос: как избавиться от динамической диспетчеризации? Как написать эффективный код?

Ответ final. Когда мы объявляем член final, он не только указывает, что он не может быть унаследован / переопределен, но также уведомляет систему о том, что - Эй! Другой подобной организации не существует. Это гарантирует, что системе не нужно беспокоиться и напрямую реализовать определенную - и это происходит во время компиляции. Таким образом, устраняются накладные расходы времени выполнения. Это называется прямым доступом или статической отправкой, который происходит во время компиляции.

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

В приведенном выше фрагменте, когда создается экземпляр User, отправляются динамические отправленные вызовы -

  1. в переменные - firstName, secondName.
  2. методам - ​​init(:), getChars(:), generateKey(:).

Кроме того, в приведенном выше фрагменте всякий раз, когда осуществляется доступ к user, это делается через динамические диспетчерские вызовы.

Здесь, несмотря на то, что существует единственный класс User, все же обязательно используется Dynamic Dispatch. Это связано с тем, что система не знает, существует ли подкласс User или какой-либо член где-либо замещен. Система может собирать эту информацию либо путем их поиска (что является динамической отправкой), либо если мы явно уведомляем ее об их отсутствии (что обрабатывается final).

Подводя итог - приведенный выше фрагмент демонстрирует общий вариант использования.

  1. Здесь члены объявлены final, чтобы предотвратить их переопределение.
  2. Их объявление final устраняет накладные расходы времени выполнения и реализует статическую отправку.

На этом пока все! Спасибо за чтение. Надеюсь, вы нашли его информативным.
Удачного кодирования 💻🖥

Вот несколько ссылок, которые позволят вам узнать больше по этой теме -

  1. Блог Apple - https://developer.apple.com/swift/blog/?id=27
  2. Различные рассылки в Swift - https://heartbeat.fritz.ai/understanding-method-dispatch-in-swift-684801e718bc
  3. Документация Swift по final - https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html #Preventing Overrides.