В общем случае время запуска приложения — это время, которое проходит от нажатия пользователем иконки приложения до отображения его интерфейса на экране. При этом выделяют следующие типы запуска:
background
или suspended
. Оно просто переводится в состояние foreground
.В общем случае процесс запуска приложения состоит из следующих шагов:
main()
приложения, и соответственно, исходного кода приложения (post-main time).Несмотря на то, что разрабатывая iOS-приложения на Swift у вас нет прямого доступа к функции
main()
, она все еще существует, но вызывается скрытно.
В первую очередь в ОС cоздается процесс запускаемого приложения, в процессе чего под него выделяется виртуальное адресное пространство.
Далее определяется, есть ли в кеше ОС образ приложения. При его наличии время запуска приложения может быть значительно уменьшено. Именно по этому запуск недавно закрытого приложения быстрее первого запуска приложения.
Подготовка образа включает в себя работу библиотеки dyld
(динамический редактор ссылок), производящей загрузку библиотек, используемых в вашем приложении.
Соответственно, чем меньше в приложении используется сторонних библиотек, тем быстрее время запуска.
Для того, чтобы измерять время, затрачиваемое на pre-main, необходимо:
Run
, в вкладке Arguments
добавить Enviroment Variables
:DYLD_PRINT_STATISTICS
со значением 1
.После этого в процессе запуска на консоли отобразится текст примерно следующего содержания:
Total pre-main time: 234.31 milliseconds (100.0%) dylib loading time: 225.03 milliseconds (96.0%) rebase/binding time: 126687488.9 seconds (141572795.5%) ObjC setup time: 10.89 milliseconds (4.6%) initializer time: 40.16 milliseconds (17.1%) slowest intializers : libSystem.B.dylib : 2.44 milliseconds (1.0%) libBacktraceRecording.dylib : 6.49 milliseconds (2.7%) libMainThreadChecker.dylib : 25.98 milliseconds (11.0%)
Post-main time включает в себя все, что происходит с приложением начиная с запуска функции main()
и в общем случае включает в себя следующие шаги:
main()
.Как говорилось ранее, несмотря на то, что вы не видите функцию
main()
в составе проекта, она добавляется на этапе компиляции и скрытно вызывается при запуске.В Swift 5.3 появился атрибут
@main
, как и@UIApplicationMain
позволяет определить точку входа в приложение, но при этом дает большую гибкость.
UIApplicationMain(_:_:_:_:)
фреймворком UIKit
. Данная функция принимает данные о запускаемом приложении и причине его запуска.UIApplication
, соответсвующий приложению, и его делегат AppDelegate
.Одному приложению всегда соответствует только один экземпляр
UIApplication
.
application(_:willFinishLaunchingWithOptions:)
.application(_:didFinishLaunchingWithOptions:)
.SceneDelegate
.scene(_:willConnectTo:options:)
, в котором создается экземпляр окна UIWindow
и происходит загрузка интерфейса.Для того, чтобы уменьшить время запуска приложения и создать положительный UX, рекомендуется исключить из этапа загрузки приложения загрузку всех необзательных данных, получение которых может быть отложено.
Если после запуска есть возможность показывать/использовать «устаревшие» данные, то отложите загрузку обновлений до этапа отображения первого интерфейса.
После запуска приложения в операционной системе создается процесс, для которого в оперативной памяти выделяется выделяется виртуальное адресное пространство.
Процесс — абстракция, объединяющее в себе независимое виртуальное адресное пространство в оперативной памяти и дополнительные ресурсы (например файлы).
Процесс — это экземпляр запущенного приложения.
Обычно запущенному приложению соответствует один процесс, но в некоторых случаях разработчиком (не под iOS) может быть реализована иной подход. Так, например, браузер Google Chrome для каждой вкладки создает отдельный процесс, чтобы исключить возможность взаимодействия вкладок между собой.
В iOS для одного приложения всегда создается только один процесс.
Процессы по умолчанию не могут взаимодействовать между собой и для организации этого взаимодействия требуется использовать дополнительные возможности, вроде общих файлов, сокетов (socket), портов (port). Так же в зависимости от используемой платформы могут быть доступны другие средства обмена. В iOS можно использовать Pipe, App Groups, Share Extension, Custom URL Scheme и другие.
В iOS и macOS все пользовательские процессы создаются процессом с названием launchd
(PID = 1). Для создания дочерних процессов в macOS можно использовать класс Process
. В iOS нет возможности самостоятельно создавать дочерние процессы.
После запуска в рамках процесс создается главный поток (thread), в рамках которого автоматически запускается RunLoop и в дальнейшем выполняются все инструкции.
В текущее время, так как iOS и приложения, написанные на Swift, все еще во многом функционируют за счет библиотек, написанных на Objective-C, в памяти устройства находится Objective-C Runtime — библиотека времени исполнения, обеспечивающая доступ ко многим функциональным возможностям операционной системы. Один из знакомых вам вариантов взаимодействия с ней является использование атрибута @objc
.