Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController variables initialization

I'm studying the swift language and I have a doubt concerning the variables initialization in a UIViewController. In my DiagramViewController I have some variables:

class DiagramViewController: UIViewController {

    var type: Constants.DiagramType
    var filename: String
    var numberOfBars: Int
    var numberOfSection: Int
    var diagramName: String

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

Swift requires an init value for those var and I can do so in many different ways, but how should I choose between these ways?

I can init the variables "inline":

class DiagramViewController: UIViewController {

    var type: Constants.DiagramType = Constants.DiagramType.HISTOGRAM
    var filename: String = "dd.txt"
    var numberOfBars: Int = 10
    var numberOfSection: Int = 5
    var diagramName: String = "Diagram"

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

I can init the variables overriding the constructor:

class DiagramViewController: UIViewController {

    var type: Constants.DiagramType
    var filename: String
    var numberOfBars: Int
    var numberOfSection: Int
    var diagramName: String

    required init(coder aDecoder: NSCoder) {
        type = Constants.DiagramType.HISTOGRAM
        filename = "dd.txt"
        numberOfBars = 10
        numberOfSection = 5
        diagramName = "Diagram"

        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

I can init the variables declaring them as Optional variables:

class DiagramViewController: UIViewController {

    var type: Constants.DiagramType?
    var filename: String?
    var numberOfBars: Int?
    var numberOfSection: Int?
    var diagramName: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        type = Constants.DiagramType.HISTOGRAM
        filename = "dd.txt"
        numberOfBars = 10
        numberOfSection = 5
        diagramName = "Diagram"

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

I can init the variables declaring them as Implicitly Unwrapped Optional:

class DiagramViewController: UIViewController {

    var type: Constants.DiagramType!
    var filename: String!
    var numberOfBars: Int!
    var numberOfSection: Int!
    var diagramName: String!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        type = Constants.DiagramType.HISTOGRAM
        filename = "dd.txt"
        numberOfBars = 10
        numberOfSection = 5
        diagramName = "Diagram"

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Why choose a method rather than another? Is there a typical pattern or a sort of standard concerning this issue? Maybe some of these solutions are cleaner than the others or even more efficient. Please help me understanding the difference between them. Thank you in advance.

like image 413
matteopuc Avatar asked Jan 03 '15 17:01

matteopuc


People also ask

Which of the following method is called after UIViewController is initialized?

A UIViewController has 3 methods that involve the initialization of it and its view: init (and init-like methods) loadView. viewDidLoad (delegate method)

How do I initialize a view controller in Swift?

Open ImageViewController. swift and add an initializer with name init(coder:image:) . The initializer accepts an NSCoder instance as its first argument and an Image object as its second argument.

What is a UIViewController subclass?

The UIViewController class defines the shared behavior that's common to all view controllers. You rarely create instances of the UIViewController class directly. Instead, you subclass UIViewController and add the methods and properties needed to manage the view controller's view hierarchy.


2 Answers

This question could be summarized down to "When should I use optionals?". There are lots of great articles and documentation on this question, but I will attempt to put together my experience with it as well as the documentation and articles I have read.

While Optionals have very specific functionality when used, I'd like to think of them more as a way of saying something about the variable itself rather than declaring functionality. When I read:

var myVar:Class? = nil

This means, that we should never anticipate that myVar is assigned and instead we should always anticipate both conditions, the first being that myVar has a value, and that it doesn't. I assume these things because of the functionality that the ? optional brings to the table. The compiler will not allow you to use myVar without unwrapping it. Because of this, the compiler suggests (whenever you access a property or function) that you use this syntax:

myVar?.myProperty = something

Because of the ? before the . this line of code will check to see if myVar is nil before unwrapping myVar and executing the line of code. Thus we have anticipated and handled both conditions. This line of code will essentially be "ignored" if myVar is nil and executed if it isn't.

This is in contrast to the other type of optional !:

myVar!.myProperty = something

That will always try to unwrap myVar. This line of code will cause an exception saying something to the effect of: "Unexpectedly found nil while unwrapping a value.". While the ? will fail silently.

If we change the declaration of myVar to use the ! optional:

var myVar:Class! = nil

Then we can always use myVar without getting the compiler error saying that we need to unwrap myVar prior to using it. For example, unlike the other optional (?), we can say:

myVar.myProperty = something

This line is equivalent to:

myVar!.myProperty = something

So if myVar is nil, then we will crash the program.

Conclusion:

Using either one of these optionals (or simply not using an optional at all) we are telling the user of myVar things about myVar because of the way the language will force or not force you to deal with myVar.

? optional var myVar:Class? = nil:

If I use the ? optional, we're essentially forcing the user to always check for nil.

! optional var myVar:Class! = nil:

If we use ! then if myVar is nil, something is wrong and we should crash the program, however, the user still has the option to handle the nil case which is especially useful if the user is the one whom was supposed to assign myVar. A great use case of this is network requests.

no optional var myVar = Class():

Not using an optional at all means (obviously) that the variable is always there and we don't ever need to worry that it is nil.

like image 147
Oxcug Avatar answered Oct 11 '22 21:10

Oxcug


The way I now deal with this was taught to me by a fellow dev, and involves using Xcode's way of dealing with this when they know a value will always be set:

For outlets you will see instance variables (as outlets) written like this:

@IBOutlet weak var twoPlayerButton: UIButton!

So if you can instantiate the class in the instance variable declaration line, then I would:

private var myClass: MyClass = MyClass()

If you cannot setup MyClass() in the instance variable line (e.g. because its constructor requires other variables to be passed in) then I would set the instance variable similar to how xcode deals with Outlets e.g.:

private var myClass: MyClass!

And then instantiate it in the viewDidLoad() method

If, however, I am not setting the value early on and/or it may get set back to nil, then I would declare it as an optional and require that anyone editing code within my class unwraps it appropriately.

like image 25
Charlie Seligman Avatar answered Oct 11 '22 21:10

Charlie Seligman