[БЕЗ_ЗВУКА] Всем привет.
Лекция посвящена экстеншенам.
Экстеншены, или расширения, добавляют новый функционал для существующего класса,
структуры, перечисления или протокола.
Можно даже расширить возможности типа, к исходным кодам которого у вас нет доступа.
Экстеншены похожи на категории из Objective-C,
но в отличие от категорий не имеют имен.
Что могут добавлять экстеншены?
Вычисляемые свойства как для типа, так и для инстанса.
Определять новые методы, создавать инициализаторы, добавлять сабскрипты,
определять и использовать новые вложенные типы,
а также добавлять поддержку протокола для существующего типа.
Для протоколов с помощью расширений можно создать готовую реализацию
для требований или добавить функционал,
которым сможет воспользоваться тип, поддерживающий протокол.
У экстеншенов есть ограничения.
Они могут добавлять новый функционал, но не могут переопределять его.
Расширения объявляются с помощью ключего слова extension,
далее следует тип, а также можно объявить о поддержке протоколов.
Расширения могут добавлять вычисляемые свойства для существующих типов,
однако нельзя добавить хранимые свойства или обозреватели для свойств.
Простой пример показан на слайде.
Мы добавили новые свойства для типа string.
Теперь для любого инстанса этого типа будет возможность использовать новый
функционал.
Следующее, что мы можем сделать с помощью расширений — это добавить новые
инициализаторы.
Это позволит создавать для типов инициализаторы с такими парметрами,
которые будут удобны для вас.
Вы можете передать в инициализатор собственный тип или расширить
инициализатор с помощью дополнительных параметров.
Добавлять можно только convenience-инициализаторы.
Designated-инициализаторы или деинициализаторы добавлять не получится,
они должны быть реализованы в оригинальной имплементации типа.
Если вы добавляете инициализатор для значимого типа,
у которого устанавливаются все значения для хранимых свойств и для него
не определяются какие-то дополнительные инициализаторы,
тогда вы можете в своей реализации вызвать инициализатор по умолчанию для этого типа.
Если вы создаете подобный инициализатор, то на вас
лежит ответственность за инициализацию для каждого из свойств для этого типа.
На слайдах показан пример, в котором определена структура.
Так как каждое свойство структуры снабжается значением по умолчанию,
мы можем создать собственный инициализатор в расширении для этой структуры и
вызвать сгенерированный автоматический инициализатор для этого типа.
Подобные решения помогут вам в создании объектов иным способом,
нежели изначально было задумано.
Помимо инициализаторов и свойств с помощью расширения можно добавить новые методы.
Берем один из стандартных типов Swift и объявляем для него новые методы.
Эти методы могут изменять непосредственно инстанс, используя ключевое слово self.
Если тип относится к значимому, то есть является структурой или перечислением,
то метод, вносящий изменения в self или изменяющий свойства этого типа,
должен быть помечен ключевым словом mutating,
точно так же как для оригинальной реализации типа.
Пример, показанный на слайде, демонстрирует,
как это должно быть выполнено.
С сабскриптами все обстоит точно так же.
Создаем расширение и описываем сабскрипт для существующего типа.
И снова, воспользуемся одним из стандартных типов Swift и добавим новые
возможности для него.
Следующей возможностью для расширения являются вложенные типы.
В данном случае нет каких-либо сложностей или ограничений со стороны языка.
Код, необходимый для реализации, показан на слайде.
Теперь хотелось бы рассказать о практическом применении расширений.
Чаще всего мы используем экстеншен для организации кода.
Конечно, вы можете написать весь код в одном блоке,
но если вы сгруппируете реализацию некоторых методов в отдельные расширения,
это поможет вам лучше ориентироваться в коде и проще читать его.
Например, мы можем создать private extension, в котором будут
реализованы только те методы, которые не должны быть доступны извне.
Пример на слайде демонстрирует подобную реализацию расширения.
Даже если мы не писали код,
то одного взгляда на него будет достаточно, для того чтобы понять,
с какими методами смогут взаимодействовать другие модули, а с какими нет.
Следующим примером может служить группировка схожих по функционалу методов.
Например, мы создали класс и выделили в отдельный экстеншен те функции,
которые, по нашему мнению, будут выполнять схожие задачи.
Отдельным случаем для группировки может служить поддержка протоколов.
Например, для реализации таблицы вам нужно поддержать
два протокола: UITableViewDataSource и UITableViewDelegate.
Почему бы не вынести поддержку и реализацию этих протоколов в два
разных расширения?
Вы смотрите на код и мгновенно понимаете, какую его часть вы хотите изменить,
или реализацию какого протокола вам в данный момент нужно посмотреть.
Помимо этого, при реализации вашего типа можно в оригинальной
имплементации определить хранимые свойства,
а в экстеншен вынести реализацию вычисляемых свойств.
Скорее всего, при разработке вы найдете удобные для вас случаи использования
расширений или почерпнете новые идеи от своих коллег.
Мы в свою очередь по мере обучения будем показывать
вам новые примеры и способы работы с расширениями.