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

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

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

P.S. Я могу ошибаться, но в Swift версии 3 поведение было иным, и выполнение таймера просто так не останавливалось. Не уверен, насколько это корректное поведение, но в любом случае в четвертом Swift происходящее вполне логично.

Теги: программирование, 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