Is There a Difference Between Global Initialization Vs Viewdidload Initialization in Swift

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

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

Instance of a class and correct initialization

You're going to be stuck until you get the scope of your instance variables corrected. In the gist you linked to, they're declared inside the @implementation { ... } block of your ViewController, but here they're declared outside the class completely, which means they're global in scope.

Inside viewDidLoad, you're redefining scene and skView in the scope of that method, hiding the global values while you do your configuration. These local variables then get deallocated at the end of viewDidLoad, so you've lost access to whatever you've set up there. (They may live on in the view hierarchy, but your global variables won't work.)

You'll want to move those global variables inside the class, and then access them using self.variableName so you don't mix them up with local variables. Other methods in the class will then also have access to what you've set up in viewDidLoad:

class GameViewController: UIViewController, UIScrollViewDelegate {
var skView: SKView!
var scene: GameScene!
var scrollView: UIScrollView!

override func viewDidLoad() {
super.viewDidLoad()

self.scene = // load the scene
self.skView = SKView(frame: CGRectMake(0.0, 0.0, 1024.0, 768.0))
self.view.addSubview(self.skView)
// etc
}
}

Finally, the "Can't add self as subview" error is happening because of this:

let skView = self.view as SKView
// ...
self.view.addSubview(skView)

how to init() a swift view controller properly?

Initialization depends on a couple of condition.

  1. If you are using storyboard, you can just remove the init and your VC will have default initializer. Make sure either all of your properties have default value or they are optional.
  2. If you are using xib or just creating view programmatically you can have custom convenience initializer where you pass some extra data this way.
class MyViewController: ViewController {
var answer: Int
convenience init(answer: Int) {
self.init()

self.answer = answer
// Do other setup
}
}

Swift, confusion with UIBarButtonItem

Well, maybe you are missing how a ViewController works inside.

First, viewDidLoad is the area were you usually setup or initialize any view or properties of the view. This method is also called only once during the life of the view controller object. This means that self already exists.

Knowing this, is important to understand what a let property does, (from Apple)

A constant declaration defines an immutable binding between the constant name and the value of the initializer expression; after the value of a constant is set, it cannot be changed. That said, if a constant is initialized with a class object, the object itself can change, but the binding between the constant name and the object it refers to can’t.

Even though the upper area is where you declare variables and constants, is usually meant for simple initialization, it's an area for just telling the VC that there is an object that you want to work with and will have a class global scope, but the rest of functionality will be added when the view hierarchy gets loaded (means that the object does not depends of self, for example, when adding target to a button, you are referring to a method inside of self)....this variables or constants are called Stored Properties

In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure. Stored properties can be either variable stored properties (introduced by the var keyword) or constant stored properties (introduced by the let keyword).

And finally, you have a lazy stored property that maybe can be applied for what you want:

A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

Solution: create a lazy var stored property or add his properties inside ViewDidLoad (when self already exists)

lazy private var doneButtonItem : UIBarButtonItem = {
[unowned self] in
return UIBarButtonItem(title: "Next", style:UIBarButtonItemStyle.Plain, target: self, action: #selector(onClickNext(button:)))
}()

OR

let rightBarButton: UIBarButtonItem?

override func viewDidLoad() {
super.viewDidLoad()
rightBarButton = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(onClickNext(button:)))
}

Global 'let' declaration requires an initializer expression

You need a variable if you're not sure what it is so that later you can change it:

private var database: Database?

There's no point in defining a constant if you're not going to actually define it as something.

iOS function to be called once (when application is initialized)

Use this one:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

It should be in your appdelegate class.

Hope it helps



Related Topics



Leave a reply



Submit