Влад Яндола @kymacat
Ментально пожилой iOS разработчик
Information
- Rating
- Does not participate
- Location
- Санкт-Петербург, Санкт-Петербург и область, Россия
- Date of birth
- Registered
- Activity
Specialization
Mobile Application Developer
Senior
SWIFT
SwiftUI
RxSwift
MVVM
UIKit
iOS development
Xcode
Autolayout
Foundation
GCD
Спасибо за фидбэк)
В голову пришел один недостаток подобного варианта: view преждевременно выгружается в память, и в случае, если контроллер добавляется в иерархию не синхронно с инициализацией, view будет бесполезно лежать в памяти до того момента, как контроллер добавится в иерархию.
В моем примере контроллер - единственный экран приложения и он сразу помещается в иерархию, поэтому проблемы с этим не будет. Тем не менее, в среднем случае лучше задавать их в viewDidLoad (так как неизвестно как контроллер будет в дальнейшем использоваться). Поправил этот момент в примере. Спасибо за замечание!
Привет. Наверное не совсем тема статьи, но почему опасно? В init с констрейнтами могут быть проблемы в случае если иерархия вьюх грузится из сторибордов и ксибов. Тут я их не использую, все вьюхи создаются программно и добавляются в иерархию перед применением констрейнтов
Насколько я понял вы говорите про функцию ниже (поправьте если я не так понял)
Тут нет обработки ошибок с помощью catch, так как подразумевается что обработка будет происходить в месте вызова данной функции (она сама по себе
throws
)При работе с async/await, непосредственно взаимодействие с потоками осуществляется через executor. Executor представляет собой сервис, который принимает задачи и выполняет их на определенных потоках. Каждая Task (или асинхронная функция) работает с определенным экзекьютором. У MainActor executor выполняет все задачи на main потоке.
Код внутри таски из примеров выполняется экзекьютором MainActor’а, следовательно на main потоке.
Из таски в примерах дергаются свойства (computed property) класса ImageLoader. У него нет привязки к @MainActor. Асинхронные задачи из этого класса будут выполняться не на main, так как запускаются другим дефолтным экзекьютором. Поэтому уже до вызова методов URLSession внутри ImageLoader код будет выполняться не в main потоке.
После возврата значения из ImageLoader, таска из viewDidLoad продолжает выполяняться на main, так как после приостановки запускается на экзекьюторе MainActor'а
Можно посмотреть вот на таком примере:
И в консоли будет:
Таска унаследовала MainActor, поэтому тут видим main поток
Доходим до точки приостановки, далее функция класса
SomeClass
планируется уже на другом экзекьюторе, который запускает код не в mainТут сразу видим, что первый принт показывает уже не main. Функция уже изначально выполняется на другом потоке. За 5 итераций цикл успел побыть на 3 разных потоках
После завершения
SomeClass().someFunc()
, оставшаяся часть таски изviewDidLoad
планируется и выполняется на MainActor экзекьюторе, и в консоли видим main потокС загрузкой изображения и побайтной обработкой идентичный флоу, просто с чуть большим количеством промежуточных шагов. На более показательных примерах планирую рассмотреть это все в следующих частях
Это учтено неявно. UIViewController, от которого мы наследуемся, помечен атрибутом MainActor. Следовательно, и наш класс наследует этот атрибут. Unstructured Task при создании наследует Actor, поэтому все UI изменения внутри него в нашем случае безопасны.
Привык называть GCD'шные очереди синхронными и асинхронными, хотя правильней говорить последовательные и конкуррентные (перевод concurrent как "параллельная" смущает, тк это тоже иной термин)
Поправил этот момент, спасибо за замечание
Добавлю еще один пункт к списку:
Если мы работаем с большим количеством потокоблокирующих тасок, то GCD будет отрабатывать такие случаи быстрее чем async/await. Например, если в async варианте функции из примера про Thread Explosion заменить
Task.sleep
на просто блокирующийsleep
, то GCD вариант выполнится быстрее. Это происходит из-за того что в Cooperative Thread Pull выделяется ограниченное кол-во потоков, и при блокировке они быстро заканчиваются.Поправил в некоторых моментах термины "многопоточность" на "асинхронность". Действительно это наверное могло запутать читателей.
Спасибо за замечание
Асинхронность и многопоточность в swift - разные понятия. Говоря про async/await - то функции выполняются асинхронно. Но в это же время асинхронная функция может быть выполнена как в одном потоке, так и многопоточно (в зависимости от реализации и контекста)