Understanding optional global variables in swift

别等时光非礼了梦想. 提交于 2020-01-15 06:32:09

问题


I'm working through a book on Swift and I understand the idea of scope in functions so what I'd like to understand next is why we set global variables using optional types in classes. Honestly it looks like we don't set these variables per say but just let the class know that there will be a variable of a specific type somewhere throughout the code base: var sut: ItemManager!.

From what I understand the variable sut is an unwrapped optional of the type ItemManger which definitely has a valid value and not nil. The reason we've set it with an exclamation mark or question mark is because there isn't an initializer within this class. What isn't clear is since this class doesn't have an initializer, what factors come into play when deciding weather to set this global variable to an optional using a questions mark or implicitly unwrapped with an exclamation mark?

import XCTest
    @testable import ToDo

    class ItemManagerTests: XCTestCase {

        var sut: ItemManager!

        override func setUp() {
            super.setUp()
            // Put setup code here. This method is called before the invocation of each test method in the class.

            sut = ItemManager.init()
        }

        override func tearDown() {
            // Put teardown code here. This method is called after the invocation of each test method in the class.
            super.tearDown()
        }


        func test_ToDoCount_InitiallySetAtZero(){

            let sut = ItemManager.init()

            XCTAssertEqual(sut.toDoCount, 0)
        }

        func test_DoneCount_InitiallySetAtZero(){

            let sut = ItemManager.init()

            XCTAssertEqual(sut.doneCount, 0)
        }
    }

回答1:


The reason we've set it with an exclamation mark or question mark is because there isn't an initializer within this class

Because when every instance of a class is initialized, it has to allocate memory for its properties/instance var. So with var sut: ItemManager!, the value of sut is nil when init. Without !, ? the compiler can't allocate, init, so you have to init manually in an initializer. That is what compiler told you.

For using ! or ?.

  • ! is used when the property/var always has value after the first assigning. And after the first time it's assigned, it can't be nil later
  • ? is used when the pro/var can have value or not



回答2:


sut is not a global variable.

sut is an instance variable of the ItemManagerTests. Every time a new instance, i.e. an object, of a ItemManagerTests is instantiated, memory is allocated for sut.

It's of type ItemManager!. This is an implicitly unwrapped optional. It's similar to ItemManager? (a.k.a. Optional), with the distinction that it is implicitly forcefully unwrapped everywhere it's used directly, as if the force unwrap operator (!) was used.

Its value is initialized to nil, but is set to a new ItemManager object when setUp is called by Xcode's testing framework.




回答3:


Firstly, sut is not a global. A global variable in Swift is declared outside any enclosing scope (So, before the class statement). sut is an instance property; each instance of your ItemManagerTests class will have a sut property.

In Swift, all non-optional properties must be initialised by the time the initialiser has completed. In some cases this can be achieved by code inside the initialiser and in other cases by assigning a default value.

In some cases, s you cannot assign or don't want to assign a default value and you can't (or don't want to) override the initialiser.

If you used a normal optional then the compiler would be satisfied that the property wasn't initialised by default or in the initialiser, but each time you referred to the property you would have to unwrap it (e.g. sut?.donecount).

Since your test case is assigning a value to sut in setup, you know that it will have a value and you could use a force unwrap (e.g. sut!.donecount), or, take it one step further and use an implicitly unwrapped optional. This allows you to refer to the property without any unwrapping, but it is still an optional and will still cause a crash if it is nil.

Note that your current code isn't even using the property, since you are allocating a local variable in your test cases. You can use:

class ItemManagerTests: XCTestCase {

    var sut: ItemManager!

    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.

        sut = ItemManager.init()
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    func test_ToDoCount_InitiallySetAtZero() {

        XCTAssertEqual(sut.toDoCount, 0)
    }

    func test_DoneCount_InitiallySetAtZero() {

        XCTAssertEqual(sut.doneCount, 0)
    }
}


来源:https://stackoverflow.com/questions/42358714/understanding-optional-global-variables-in-swift

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!