Search code examples
swiftscopeinitializationviewdidload

Is there a difference between global Initialization vs viewDidLoad initialization in Swift


If I have a class and initialize a variable like so:

class TestClass: UIViewController {
    var thisInt: Int = 10
}

is that any different than initializing like so:

class TestClass: UIViewController {
    var thisInt: Int!

    override func viewDidLoad() {
        super.viewDidLoad()
        thisInt = 10
    }
}

I suppose my main questions lie in when the global initialization takes place, and is there a time when one would get called more than the other would be called with normal iOS Programming (not doing anything drastically against native development). I do understand that doing it in viewDidLoad restricts me to using weak or an optional, but I'm more concerned about any other differences.


Solution

  • You can find out easily by adding a "computed" property to your view controller:

    class TestClass: UIViewController {
        let a = {()->Int in print("global initialization"); return 10 }()
    }
    

    and adding a

    print("didFinishLaunching")
    

    in app's delegate didFinishLaunchingWithOptions method.

    The order you'll get is

    global initialization
    didFinishLaunching
    

    which means global initializers run before the app lifecycle begins.

    Now, to go even further, you could add a main.swift file with the following contents

    print("Before UIApplicationMain")
    
    UIApplicationMain(CommandLine.argc, unsafeBitCast(CommandLine.unsafeArgv, to: UnsafeMutablePointer<UnsafeMutablePointer<Int8>>.self), nil, NSStringFromClass(AppDelegate.self))
    

    and remove (or comment) the @UIApplicationMain decoration from your AppDelegate class. This will instruct the compiler to use the code in main.swift for the application initialization instead of the default behavior provided by the decorator in discussion (although we're providing a custom almost identical implementation).

    What you'll get in this 2nd approach is

    Before UIApplicationMain
    global initialization
    didFinishLaunching
    

    which means that the instance property code is executed when the storyboard is loaded.

    Now, for more insight, let's try to find out the differences between static an instance variables. For this we'll add a test class:

    class PropertyInitializationTest {
        static let staticProp = {()->String in print("static initialization of InitializationTest"); return "" }()
        let instanceProp = {()->String in print("instance initialization of InitializationTest"); return "" }()
    
        init() {
            print("In initializer")
        }
    }
    

    , and update AppDelegate's did finish launching:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
            print("didFinishLaunching")
            print("before instantiating InitializationTest")
            _ = PropertyInitializationTest()
            print("after instantiating InitializationTest")
            // Override point for customization after application launch.
            return true
        }
    

    The output we get is:

    Before UIApplicationMain
    global initialization
    didFinishLaunching
    before instantiating InitializationTest
    instance initialization of InitializationTest
    In initializer
    after instantiating InitializationTest
    

    , which confirms the fact that instance properties get set when the class is instantiated, and before any initializer code runs.

    But wait! What about the static property? There are no traces to indicate that it was initialized at all. Looks like static properties are lazy by definition, and get initialized only when accessed the first time.

    Updating the app did finish launching code confirms this.

    print("didFinishLaunching")
    print("before instantiating InitializationTest")
    _ = PropertyInitializationTest()
    print("after instantiating InitializationTest")
    _ = PropertyInitializationTest.staticProp
    print("after instantiating InitializationTest")
    

    gives the following output:

    Before UIApplicationMain
    global initialization
    didFinishLaunching
    before instantiating InitializationTest
    instance initialization of InitializationTest
    In initializer
    after instantiating InitializationTest
    static initialization of InitializationTest
    after instantiating InitializationTest
    

    To conclude:

    • instance properties receive the compile-time values (if set) before the class initializer runs, thus before any code from that class gets executed
    • static properties receive their values only when firstly accessed, they are lazy by nature