Всем доброго дня! С вами Анна Жаркова, ведущий мобильный разработчик компании Usetech. Продолжаем рассматривать способы многопоточный работы в Kotlin Native. В предыдущих статьях мы уже рассмотрели существующие способы работы с многопоточностью с корутинами и без, и что делать с имеющимися болями. Теперь поговорим о новой модели управления памятью, которая появилась совсем недавно. 31 августа компания JetBrains представили превью новой модели управления памятью в Kotlin Native.  Основной упор команда разработчиков сделала на безопасность шаринга между потоками, устранение утечек памяти и освобождение нас от использования специальных аннотаций. Также доработка коснулась Coroutines, и теперь можно без опаски переключаться между контекстами корутин без заморозки. Обновления подхватили и Ktor:  Итак, что же нового появится в версии Kotlin 1.6.0-M1-139: 1. Заявлено, что мы можем убрать все freeze() блоки (в том числе и во всех фоновых Worker), и переключаться между контекстами и потоками без каких-либо проблем.  2.Использование AtomicReference или FreezableAtomicReference не приводит к утечкам памяти. 3.При работе с глобальными константами не нужно теперь использовать SharedImmutable. 4.При работе с Worker.execute producer больше не требует возвращать изолированный подграф объектов.  Однако есть и нюансы:1. Необходимо оставлять заморозку при работе с AtomicReference. В качестве альтернативы мы можем использовать FreezableAtomicReference или AtomicRef из atomicfu . Однако, нас предупреждают, что atomicfu еще не достигла версии 1.х.  2.При вызове suspend функции Kotlin в Swift  ее completion handler блок может не прийти в main thread. Т.е добавляем DispatchQueue.main.async{...}, если нам нужно.  3.deInit Swift/ObjC объктов может вызываться в другом потоке.  4.Глобальные свойства инициализируются лениво, т.е при первом обращении. Ранее глобальные свойства инициализировались при запуске. Если вам нужно поддерживать это поведение, то добавляем теперь аннотацию @'EagerInitialization. Рекомендовано ознакомиться с документацией перед использованием.Нюансы есть и в работе с корутинами,  в версии поддерживающей новую модель управления памятью:1.Мы можем работать в Worker с Channel и Flow без заморозки.  И в отличии от native-mt версии заморозка, например, канала заморозить все его содержимое, что может не ожидаться. 2.Dispatchers.Default теперь поддерживается global queue. 3.newSingleThreadContext и newFixedThreadPoolContext теперь можно использовать для создания диспетчера корутин с поддержкой пула одного или нескольких разработчиков. 4.Dispatchers.Main связан с main queue для Darwin и отдельным Worker для других платформ Native. Поэтому рекомендовано не использовать его для работы с Unit тестами, так как ничего не будет вызвано в очереди главного потока.Нюансов много, есть определенные проблемы с перформансом и известные баги, о которых команда разработки написала предупредительно в документации. Но это пока превью (даже не альфа).Что ж, давайте попробуем настроить наше решение из предыдущих статей под новую версию модели управления памяти. Для установки версии 1.6.0-M1-139 добавим некоторые настройки: И разумеется, добавим зависимость для корутин: Важно! Если у вас не установлен Xcode версии 12.5 или выше, обязательно скачайте и поставьте. Это минимальная совместимая версия с 1.6.0-M1-139. Если у вас установлено уже несколько версий Xcode, в том числе и более низкие, то поменяйте на подходящую с помощью xcode-select, закройте проект Kotlin Multiplatform и запустите Invalidate cache and Restart. Иначе получите ошибку о несовместимости версии.  Начнем с уборки freeze() блоков из бескорутиновой версии: Также уберем заморозку с параметров, которые мы используем для UrlSession (у нас нативный сетевой клиент): Для полного избавления от заморозок меняем AtomicReference на FreezableAtomicReference: И подправляем код, где мы атомарные ссылки используем: Код так и дышит чистотой и просто летает, несмотря на то, что GC (в котором могут быть боли) у нас не поменялся.Теперь подтюнингуем пример с корутинами: Для начала мы воспользуемся диспетчерами по умолчанию. Чтобы проверить магию GlobalQueue, выведем данные о контексте в блоке под управлением ioDispatcher: Убираем заморозки при работе с Flow и/или Channel:  Все работает, отлично и быстро.  Не забываем вынести ответ в очередь main thread: Важно! Для предотвращения утечек на стороне  iOS, особенно в случае большого количества различных объектов Swift/ObjC, и вспоможения GC оборачиваем блоки вызова и работы с ответом в  autoreleasepool. Теперь попробуем следующее. Запустим на MainScope, но с помощью newSingleThreadContext укажем другой фоновый диспетчер: Все отрабатывает без запинок. С наших разработческих плеч совсем скоро свалится гора забот. Но остается жирное "НО". Не все библиотеки, которые мы используем в приложениях KMM, готовы к новой модели памяти, новому подходу к заморозкам и передаче между контекстами. Мы можем получить исключение InvalidMutabilityException или FreezingException.Поэтому для них в приложениях с версией 1.6.0-M1-139 придется отключить встроенную заморозку: Более подробно о новой версии модели управления памятью смотрите здесь: https://github.com/JetBrains/kotlin/blob/master/kotlin-native/NEW_MM.md И сэмпл на коленке: https://github.com/anioutkazharkova/kotlin_native_network_client/tree/feature/1.6-kn/sample Lead mobile developer в Usetech, Kotlin GDE