The Swift Awaitening: Методичное пособие

20 сентября 2021 года состоялся релиз версии 5.5 языка Свифт. В ней программистам стало доступно множество новых фич, связанных с асинхронностью и concurrency (не представляю, как это правильно перевести на русский; «параллелизм» слишком уж как-то тяжко произносится).

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

Главная и корневая фича, конечно, — async/await, то есть возможность создавать асинхронные функции, которые обрабатываются процессором несколько иначе, чем обычные функции. Прямо как в Джаваскрипте или Си Шарпе, но, как водится, лучше.

Теги: swift, concurrency, async, await

Как я перестал бояться и полюбил EventLoopFuture

Низкоуровневый фреймворк SwiftNIO, разработанный компанией Эппл, лежит в основе всех крупных серверных фреймворков на языке Свифт — Vapor, Kitura, Smoke.

Однако ни один из этих инструментов не разъясняет в полной мере концепций и особенностей использования самой главной части NIO — EventLoopPromise и EventLoopFuture, что вызывает у конечных программистов фундаментальное непонимание всего происходящего, оттуда и вопросы во всех чатах и форумах вроде «А как достать значение из Future?».

Об этом и поговорим.

Теги: swift, swift-nio, vapor, future, promise, EventLoopFuture, EventlLoopPromise

Почему не работает таймер в Swift?

Предположим, вы сделали

и отправились по своим делам, а таймер отработал один раз или вообще не отработал. Почему? А потому, что объект timer зажевал сборщик мусора, а при деинициализации DispatchSourceTimer, разумеется, останавливает своё дальнейшее выполнение. Напомню, сборщик мусора всегда отрабатывает в конце области выполнения, то есть, в данной ситуации, по окончанию блока, переданного в async. Таким образом, если вы хотите регулярно выполнять какую-то задачу, необходимо класть указатель на объект таймера в известное глобальное место, которое проживет столько, сколько захочется вам, а не сборщику мусора. Желательно также организовать синхронный и потоко-безопасный доступ к этому месту с помощью последовательной очереди выполнения, чтобы избежать постыдных гонок за доступ.

Кстати, следует отметить, что существует и обратная ситуация: таймер отказывается удаляться, когда вы удалите все ссылки на него. Это может быть оттого, что хендлер таймера может держать сильную ссылку на какой-нибудь класс или self. Чтобы этого избежать, указывайте [weak self] в начале хендлера таймера.

Теги: программирование, swift

Секрет Self в Swift

Обновление 2019 года: в версии 5.1 Self можно будет использовать в конкретном контексте, а не только в протоколе и его расширениях. Как только версия 5.1 официально зарелизится, я обновлю эту заметку. А пока она отражает реалии Свифта до версии 5.1.

Изучающие язык программирования Swift наверняка знают, что означает ключевое слово self — это указатель на текущий объект внутри экземпляра класса. Совершенно нормальный функционал практически любого объектного языка. Сложности у обывателя могут начаться в момент, когда он сталкивается со статическими методами и свойствами, а также со знаменитой протокольно-ориентированной парадигмой этого языка. Не будем акцентироваться на особенностях протокольной философии, а приступим к самому интересному, с чем наверняка сталкивался всякий.

Я говорю о вызове статического метода из расширения протокола или определении метода протокола, который должен вернуть конечный тип (eventual type). Всё это является частным случаем позднего статического связывания, о котором всякий может нагуглить достаточно материала.

В случае с банальными статическими методами внутри класса или структуры всё просто — метод легко вызвать по названию типа или вызвать функцию type(of: self), которая вернет текущий тип, у которого уже можно вызывать статические методы. Однако если конкретного типа еще нет, а есть только протокол и/или его расширения (то бишь метатип), то язык не даст нам этого сделать, ведь протокол не может содержать реализации, а расширение протокола не считается конкретным кодом, и мы получаем ошибку Static member 'staticMethodName' cannot be used on protocol metatype 'ProtocolName.Protocol'.

Решением является использование ключевого слова Self (именно с большой буквы), которое является указателем на конечный тип, который реализует протокол. В официальной документации языка есть только одно упоминание этого ключевого слова:

In that [protocol] context, Self refers to the eventual type that conforms to the protocol.

Соответственно, работает это ключевое слово только в метатипах (протоколах и их расширениях), а в конкретных классах или структурах вы получите ошибку Use of unresolved identifier 'Self'.

Теги: программирование, swift, self