iOS App Launch, Static binding vs Dynamic binding, Linking vs Embedded

Two important factors that determine the performance of apps are their launch times and their memory footprints. Reducing the size of an…

iOS App Launch, Static binding vs Dynamic binding, Linking vs Embedded

Two important factors that determine the performance of apps are their launch times and their memory footprints. Reducing the size of an app’s executable file and minimizing its use of memory once it’s launched make the app launch faster and use less memory once it’s launched.

Improving iOS app launch time

It takes some time to launch a mobile app, especially on a system with limited power of mobile CPU. Apple suggests 400ms as a good launch time. iOS performs zoom animation during the app launch — thus creating an opportunity to perform all CPU-intensive tasks. Ideally the whole launch process on iOS should be completed as soon as the app opening animation ends.

Apple engineers described some techniques to improve launch times in WWDC 2016 — Session 406: Optimizing App Startup Time, WWDC 2017 — Session 413: App Startup Time: Past, Present, and Future. This wasn’t enough, so this year they announced a brand new dynamic linker in WWDC 2019 — Optimizing App Launch. Looking at the history of dyld3, one can see that Apple is constantly trying to make their operating systems faster.

Why Launch Is Important

  • First experience with your app should be delightful
  • Indicative of your code’s overall performance
  • Impacts the system performance and battery

App Launch Types

Reference: https://developer.apple.com/videos/play/wwdc2019/423/

Phases of App Launch

1. System Interface - DYLD3

• Dynamic Linker loads shared libraries and frameworks
• Introduces caching of runtime dependencies to improve warm launch

Benefits:
* Avoid linking unused frameworks
* Avoid dynamic library loading during launch
* Hard link all your dependencies

2. Runtime Init(libSystem Init)

  • Initializes the interfaces with low level system components
  • System side work with a fixed cost

Static Runtime Initialization

  • Initializes the language runtime
  • Invokes all class static load methods

Benefits:

  • Expose dedicated initialization API in frameworks
  • Reduce impact to launch by avoiding +[Class load]
  • Use +[Class initialize] to lazily conduct static initialization

3. UIKit Initialization

  • Instantiates the UIApplication and UIApplicationDelegate
  • Begins event processing and integration with the system

Benefits:

  • Minimize work in UIApplication subclass
  • Minimize work in UIApplicationDelegate initialization

4. Application initialization

Lifecycle Callbacks

  • Invokes UIApplicationDelegate app lifecycle callbacks application:willFinishLaunchingWithOptions: application:didFinishLaunchingWithOptions:
  • Invokes UIApplicationDelegate UI lifecycle callbacks applicationDidBecomeActive
  • Invokes UISceneDelegate UI lifecycle callbacks for each scene scene:willConnectToSession:options:
    sceneWillEnterForeground:
    sceneDidBecomeActive:

Benefits:

  • Defer unrelated work
  • Share resources between scenes

5. Initial Frame Render

First Frame Render

  • Creates, performs layout for, and draws views
  • Commits and renders first frame
loadView 
viewDidLoad
layoutSubviews

Benefits:

  • Flatten view hierarchies and lazily load views
  • Optimize auto layout usage

6. Extended

  • App-specific period after first frame
  • Displays asynchronously loaded data
  • App should be interactive and responsive

Benefits:

  • Leverage os_signpost to measure work

Static binding

Static binding is where the item being referred to is determined at compile time. In (Objective-)C(++) a call to a function is statically bound; for example the library function NSLog. Also references to variables are static in these languages; which variable is being referenced is determined completely at compile time. Static binding cannot fail at runtime, it is a compile time error to fail to determine what a statically bound reference refers to.

Late binding

This is where the exact item being referred is determined at runtime. This typically, but not always, comes about when a language supports inheritance/subtyping where a type T may be a subtype/child/subclass (terminology depends on language) of a type S. This means that a value of type T has all the attributes of a value of type S, and in object-oriented languages that includes methods, and that a value of type T can be treated as a value of type S.

Dynamic binding

Dynamic binding is determining the method to invoke at runtime instead of at compile time. Dynamic binding is also referred to as late binding.This is a step beyond late binding where it is left to runtime to determine if the referenced item exists.

Framework

A framework is a shared library packaged with associated resources, such as headers, localized strings, and documentation, installed in a standard folder hierarchy, usually in a standard location in the file system. The folders usually contain related header files, documentation in any format, and resource files. A framework may contain multiple versions of itself, and each version may have its own set of resources, documentation, and header files.

Static libraries (*.a) are collections of object files. In its turn, object file is just a name for a file that comes out of a compiler and contains machine code.

Dynamic libraries (*.dylib) as opposed to the static ones, rather than being copied into single monolithic executable, are loaded into memory when they are actually needed. This could happen either at load time or at runtime.

Binary Frameworks

A binary framework is already compiled source code with resources with a defined interface that you can use in your apps. It comes in two flavors: a static library and dynamic framework.

Link Binary With Libraries from Build Phases is a mirror of Linked Frameworks and Libraries from General.

  1. If you add a Static Library to this section Static Linker at compile time will include all code from the library into the executable object file

If you add a Dynamic Framework to this section Dynamic Linker at load or run time will try to find the framework using system loader path inside the system and link it.

Embed Frameworks from Build Phases is a mirror of Embedded Binaries from General. Embedding actually adds a copy of the framework into your application bundle under the Frameworks directory by default. Your application won’t be able to do anything with that Framework unless it links to it. Xcode takes the liberty of doing this for you. As a result, when a framework is added/removed to Embed section it will be automatically added/removed to Linked section.

Link Binary With Libraries” means what you’d expect it to with respect to linkage: Regardless of whether the binary is a static library, dynamic library, or framework it will be linked to your object code at link time after compilation.

When you think of linkage with a static library, what happens is pretty clear: the linker copies the code from the library (e.g. libFoo.a) into your output binary. Your output file grows in size but doesn't need to resolve any external dependencies at runtime. Everything your program needs to run (with respect to the static library) is present after it is built.

With a dynamic library (.dylib, or system-supplied framework), the expectation is that the library you are linking against will be present somewhere in the system’s dynamic-library loader path when you run your program. This way you don’t have the overhead of copying all the third party external libraries into your binary, and all the different programs on a computer that also link to that library will be able to find it, which saves minimally disk space, but also potentially memory space, depending on how and where the system caches libraries.

Dynamic linker performs a lot of disk IO when searching for dependencies. Static linking eliminates the need for all that dylib searching — dependencies and executable become one.

A framework is much like a dynamic library, but can contain resources in its directory structure (images, audio, other frameworks, etc.). In this case a simple static-library or .dylib file won’t cut it so you might have to link to a framework just so it can find what it needs to run properly.

The question you linked references the “Link Binary With Libraries” functionality, which is somewhat different than an embedded binary.

“Link Binary With Libraries” means what you’d expect it to with respect to linkage: Regardless of whether the binary is a static library, dynamic library, or framework it will be linked to your object code at link time after compilation.

When you think of linkage with a static library, what happens is pretty clear: the linker copies the code from the library (e.g. libFoo.a) into your output binary. Your output file grows in size but doesn't need to resolve any external dependencies at runtime. Everything your program needs to run (with respect to the static library) is present after it is built.

With a dynamic library (.dylib, or system-supplied framework), the expectation is that the library you are linking against will be present somewhere in the system’s dynamic-library loader path when you run your program. This way you don’t have the overhead of copying all the third party external libraries into your binary, and all the different programs on a computer that also link to that library will be able to find it, which saves minimally disk space, but also potentially memory space, depending on how and where the system caches libraries.

A framework is much like a dynamic library, but can contain resources in its directory structure (images, audio, other frameworks, etc.). In this case a simple static-library or .dylib file won’t cut it so you might have to link to a framework just so it can find what it needs to run properly.

When you link to a third-party framework (say something you downloaded from github and built yourself), it might not be present on the system you intend to run on. In this case, you’d not only link to the framework, but embed it inside your application bundle as well using the “Copy Frameworks” phase. When your program runs, the runtime-linker (aka the resolver) will look inside your bundle in addition to the system loader path, find the embedded framework, and link it so your app will have the code it needs in order to run.

For Demo: DynamicFrameworkDemo

Next: Implementing Dark Mode in iOS 13

References:

  1. https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/DynamicBinding.html
  2. https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkBinding.html