One of the more interesting features of Objective-C is the ability to send messages to non-existent objects, so
...
NSString* path = nil;
BOOL isTxtFile = [[path lastPathComponent] hasSuffix:@".txt"];
if (isTxtFile)
{
printf("is text file\n");
}
else
{
printf("is not text file\n");
}
...
prints
is not text file
Depending upon your language of choice this kind of thing usually results in an NPE or the abrupt termination of your program, but in Objective-C nothing happens.
The reason nothing happens in Objective-C is because the semantics of sending a message to nil
are well defined.
If you send a message which returns nothing, i.e., void
, nothing happens.
if you send a message which returns a reference to object to nil
then you get back nil
.
If you send a message which returns something other than an object to nil you get back 0
or its equivalent
In the example above the call to lastPathComponent on nil
returns nil
.
The call to hasSuffix on nil
returns false.
The downside to this is that you can, on occasion, spend quite a bit of time wondering why nothing is happening only to eventually discover that some variable had the value nil
when it ought not to have.
The designers of Swift have decided to be helpful and eliminate this downside by making it both impossible to forget to initialize variables and impossible to initialize them to nil
in the Objective-C style.
1.0 Constants And Variables
You can declare a constant in Swift like this
let constantCod: String = "a fish"
Swift supports type inference so you can omit the type if you wish
let constantCod = "a fish"
As you might expect given that it is a constant you cannot assign another value to constantCod
once it has been initialized.
In addition if the constant refers to a compound value, an instance of a struct for example, the value itself is immutable.
You declare a variable in Swift in the same way but using var instead of let.
var cod: String = "a fish"
and without the type.
var cod = "a fish"
You can assign another value to a variable in the usual way
cod = "another fish"
If you are declaring a constant or a global variable you must specify a value in the declaration, but if you are declaring a local variable you can omit the initializer if you wish in which case you must specify the type.
If you declare a local variable without an inititializer you cannot access that variable until it has been explicitly initialized.
So doing this
var cod: String
...
// compiler error here: Variable 'cod' used before being initialized
var uppercaseCod = cod.uppercaseString
println("uppercaseCod == \(uppercaseCod)")
gets you a compiler error, whereas, doing this
var cod: String
cod = "a fish"
var uppercaseCod = cod.uppercaseString
println("uppercaseCod == \(uppercaseCod)")
prints
uppercaseCod == A FISH
as you might expect.
2.0 The Optional Type
While this is all very helpful, what if you really, really need to declare a variable which has a value except when it doesn’t ?
The answer is to declare the variable to have the type
Optional<T>
where T
is the type of the variable when it does have a value.
For example
var optionalCod: Optional<String>
The generic type Optional<T>
is defined in the Swift library.
Swift provides syntactic sugar for this in the form of question mark (‘?’) suffix to the type of the variable so you can write the above as
var optionalCod: String?
3.0 Assignment
You can assign three types of thing to a variable of type Optional<T>
-
a value of type T
-
nil
which indicates that the variable has no value, or -
another value of type
Optional<T>
Omitting the initializer when declaring an Optional variable
var optionalCod: String?
is equivalent to explicitly initializing it to nil
var optionalCod: String? = nil
If you assign one Optional<T> value to another the value will be copied. so
var cod : String? = "a fish"
var dab : String? = cod
println("cod == \(cod)")
cod = nil
println("cod == \(cod)")
println("dab == \(dab)")
prints
cod == a fish
cod == nil
dab == a fish
4.0 Accessing The Value
Now you have succeeded in declaring a variable which may or may not have a value how do you get at the value, assuming there is one ?
In Swift parlance this is termed unwrapping and there are multiple ways of doing it.
4.1 Forced Unwrapping
If you are absolutely certain that there is a value in the variable you can use forced unwrapping which simply involves adding an exclamation mark (!) as a suffix
optionalCod!
This is definitely the way to go if you like your programs to terminate abruptly when something goes wrong because if there is no value in the variable that is exactly what happens
So doing this
var optionalCod: String?
...
var cod : String = optionalCod!
gets you this at runtime
fatal error: Can't unwrap Optional.None
and everything stops.
4.2 Look Before You Leap
Fortunately for those of us of a more nervous disposition it is possible to check whether an Optional variable contains a value before potentially exploding it using the bang operator !
For example
var optionalCod: String?
...
if optionalCod
{
println("uppercase optionalCod == \(optionalCod!.uppercaseString)")
}
else
{
println("optionalCod is nil")
}
4.3 Optional Binding
Alternatively you can combine the checking and the unwrapping like so
var optionalCod: String?
if let cod = optionalCod
{
println("uppercase cod == \(cod.uppercaseString)")
}
else
{
println("optionalCod is nil")
}
4.4 Optional Chaining
If you are invoking a method on the value in an Optional variable you can add a question mark (‘?’) suffix to the variable and then invoke the method, so given
var path: String?
you can do
path?.componentsSeparatedByString("/")
If the variable contains a value then the method will be invoked. If it is nil then nothing happens.
The value of this expression will of necessity be of type Optional<T>
where T is the type of the value returned by the method being invoked
var path: String?
...
let parts: Optional<String[]> = path?.componentsSeparatedByString("/")
...
Given that the type of the expression is of type Optional<T>
you might think that if you want to call a method on the result you need to use the ‘?’ suffix again like so
path?.componentsSeparatedByString("/")?.reverse()
but if you try this you get a compiler error, specifically
path?.componentsSeparatedByString("/")?.reverse()
Operand of postfix '?' should have optional type: type is 'String[]'
which is a bit puzzling at first.
In fact while it is true that in isolation the type of
path?.componentsSeparatedByString("/")
is
Optional<String[]>
in the context of a chain of method calls if the Optional variable has no value then the ? operator shortcuts the entire chain and returns immediately.
So at runtime at the point the method reverse is being called the ? operator has already opened the box and found a
String[] value in it rather than nil and it has called componentsSeparatedByString on it and the type of
path?.componentsSeparatedByString("/")
really is
String[]
Another way of looking at it is that adding a postfix ? is equivalent to doing this
let path : String?
var result: (String[])?
// ...
if let p : String = path
{
let parts : String[] = p.componentsSeparatedByString("/")
let reversed: String[] = parts.reverse()
result = reversed
}
else
{
result = nil
}
from which it is clear that the result of calling componentsSeparatedByString really is a String[].
Which is all a very long and round about way of you do not actually need the second postfix ? at all. You can simply do this
path?.componentsSeparatedByString("/").reverse()
You can chain as many methods together as you can find and you still only need the initial ‘?’ assuming of course that none of the methods themselves explicitly return Optional values.
If the last method in the chain returns a value of type Optional<T>
, for example
path?.componentsSeparatedByString("/").reverse()[0].toInt()
where the method toInt returns
Optional<Int>
you might think that the result of the method chain expression would be
Optional<Optional<T>>
but it is in fact
Optional<T>
which has the merit of consistency and simplicity since you only ever have to unwrap the resulting value once
4.5 Implicit Unwrapping
If you declare an Optional variable then, as we have seen, you need to unwrap it one way or another each time you want to use the value.
If you are in a position where you are certain that the Optional variable will definitely have a value each time you use it then you can avoid the need to explicitly unwrap it at each point of use
by declaring it like this
var optionalDab: String!
adding a postfix exclamation mark (!) to the type rather than a question mark (?).
You can then refer to it directly when using without explicitly unwrapping it.
It is still being unwrapped implicitly using forced unwrapping so if your model of the variable’s usage is incorrect and there is in fact some point at which it can be used before it has been initialized you will find out all about it at runtime.
5.0 Optional Constants
You can, if you want, declare constants with Optional types but after the novelty has worn off they turn out not to be very interesting at all.
Since the only thing you can do with a constant is initialize it or use it you can either have a constant which is guaranteed to be nil
let constantDab: String?
or one that is guaranteed to have a value
let constantDab: String? = "an optional fish"
and thats it.
6.0 Optional Globals
You can declare a global variable with an Optional value.
This can be useful if you do not want to or cannot initialize it at the point it is declared but it is guaranteed to have been initialized before it is used.
This case also allows you to take advantage of implicit unwrapping as described above but the caveat about runtime exceptions also applies.
// declaration
var globalOptionalEel: String!
...
// initialization
globalOptionalEel = "global eel"
...
// usage with implict unwrapping plus chance of runtime exception
let constantEel: String = globalOptionalEel
Copyright (c) 2014 By Simon Lewis. All Rights Reserved.
Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.
Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.