Optionals are a very powerful feature of the Swift programming language that allow developers to answer a common question in object oriented programming: “What happens when this thing doesn’t get initialized before it’s used?”
In Objective-C, if a object was uninitialized before an attempt was made to use it, it would have a value of nil. The key phrase here is “value of nil:” nil represented “the object that isn’t an object” or “an uninitialized object” in Objective – C. In other words, nil was an object. An uninitialized object, but an object nonetheless.
In Swift, nil means “nothing at all.” It’s not an object, it’s not a primitive, it’s not a zero… it’s nothing. In fact, in Swift, nil means “any kind of nothing.” Nil can mean that an object or struct or enum or Int or Double … or whatever … is nothing at all. Before we get too far along in this existential ramble, perhaps we should answer the question of the day: “If nil is nothing at all, what is nothing at all good for?”
Consider the following declaration:
1 | var myInt : Int |
The intention is to declare a variable myInt as type Int that will be assigned a value later. The type of Int guarantees that myInt will have a value: myInt’s type is Int.
But what will happen if we attempt to access the value of myInt before it gets a value? Since there is no automatic initialization in Swift, we get a compile – time error:
1 2 | var myInt : Int let yourInt = myInt |
The error is “variable myInt used before being initialized.” This is easy to understand: in order to assign the value of a variable to something else, the variable must have a value in the first place. But does myInt have a nil value here? Let’s check:
1 2 3 | if myInt == nil { println ( "myInt is nil" ) } |
Again we get an error: “Cannot invoke ‘==’ with an argument list of type (@lvalue Int, NSLiteralConvertable)” This means that myInt is of type Int, whereas nil is of no type. We can’t compare them. What we need here is some kind of type that can either be an Int or nil, depending on whether it’s been initialized. And that’s what an optional is.
If we declare myInt as
1 | var myInt : Int ? |
We are saying “myInt is a variable of type optional Int.” The question mark makes the type — any type — an optional: at runtime, the value of an Int? may be either an optional Int or nil. With this declaration, we can make the comparison, and do something about initializing myInt if it doesn’t already have a value:
1 2 3 | if myInt == nil { myInt = 8 } |
After this runs, myInt has a value. But the type of myInt is an optional Int, not an Int. The value of myInt is now “an optional integer with the value of 8.” In a playground, myInt’s value is displayed as (Some 8), meaning myInt has some value, and it is 8.
We can get to the actual value of an optional by unwrapping it. The unwrapping operator for optionals is !, this code:
1 | println ( "The value of myInt is \(myInt!)." |
prints “The value of myInt is 8.” This form of unwrapping is called explicit unwrapping: the var was declared (or returned) as an optional, and we want it’s actual value. We can also declare an optional to be implicitly unwrapped by declaring it with a ! instead of a ?. Implicit unwrapping allows us to get the actual value of an optional without explicitly unwrapping it:
1 2 3 4 5 6 7 | var myInt : Int ! if myInt == nil { myInt = 8 } println ( "The value of myInt is \(myInt)." ) |
prints “the value of myInt is 8.”
Knowing your way around optional syntax is especially important when working with the iOS Foundation, UIKit, and other frameworks. One obvious use of optionals is when creating outlet properties in View Controller subclasses. Outlet properties are references to subviews on the view: when they are created in the view controller, the view has not yet been loaded. Such properties will be nil until the view loads, so we must declare them as optional vars:
1 | @ IBOutlet var myLabel : UILabel ? |
This is because if we try to access them before the view loads, we want to know about it! myLabel will be nil (and not of type Optional UILabel) until it actually references an object on the view. We can check for that an respond accordingly.
Of course, if we declare myLabel as above, we’ll have to explicitly unwrap it when we need to set its properties, or use optional chaining syntax:
1 | myLabel ? . text = "Hello!" |
which is a way of saying “if myLabel isn’t nil, set its text to “Hello!” If we declare myLabel as an implicitly unwrapped optional:
1 | @ IBOutlet var myLabel : UILabel ! |
then we can get directly to it’s text property:
1 | myLabel . text = "Hello!" |
But be careful: trying to unwrap an optional whose value is nil is a runtime error, whether we’re using implicit or explicit unwrapping.
There is also a special if statement syntax that allows us to check for a value in an optional and assign that value to a constant:
1 2 3 | if let someLabel = myLabel { someLabel . text = "Label text goes here." } |
Even if myLabel is declared as type UILabel?, someLabel is of type UILabel, and we can directly set its text without unwrapping it.
Many objects in the iOS 8 frameworks are passed around as optionals. For example, the value returned for a particular key of a Dictionary is returned as an optional, and must be unwrapped. We can use explicit unwrapping, optional chaining, or if-let syntax to do this.
The more you work with optionals, the more possibilities you’ll find for them: they decrease the likelihood of runtime errors, make many tasks easier, and allow you to write very tight, efficient code. Optionals may be many things, but they’re certainly not optional!
Không có nhận xét nào:
Đăng nhận xét