Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between String? and String! (two ways of creating an optional variable)?

Tags:

ios

swift

People also ask

What does string mean in Swift?

A string is a series of characters, such as "Swift" , that forms a collection. Strings in Swift are Unicode correct and locale insensitive, and are designed to be efficient. The String type bridges with the Objective-C class NSString and offers interoperability with C functions that works with strings.

What optional binding?

Optional binding is a mechanism built into Swift to safely unwrap optionals. Since an optional may or may not contain a value, optional binding always has to be conditional. To enable this, conditional statements in Swift support optional binding, which checks if a wrapped value actually exists.


The real benefit to using implicitly unwrapped optionals (declared with the !) is related to class initialisation when two classes point to each other and you need to avoid a strong-reference cycle. For example:

Class A <-> Class B

Class A's init routine needs to create (and own) class B, and B needs a weak reference back to A:

class A {
    let instanceOfB: B!
    init() {
        self.instanceOfB = B(instanceOfA: self)
    }
}

class B {
    unowned let instanceOfA: A
    init(instanceOfA: A) {
        self.instanceOfA = instanceOfA
    }
}

Now,

  • Class B needs a reference to class A to be initialised.
  • Class A can only pass self to class B's initialiser once it's fully initialised.
  • For Class A to be considered as initialised before Class B is created, the property instanceOfB must therefore be optional.

However, once A's been created it would be annoying to have to access instanceOfB using instanceOfB! since we know that there has to be a B

To avoid this, instanceOfB is declared as an implicity unwrapped optional (instanceOfB!), and we can access it using just instanceOfB. (Furthermore, I suspect that the compiler can optimise the access differently too).

An example of this is given on pages 464 to 466 of the book.

Summary:

  • Use ? if the value can become nil in the future, so that you test for this.
  • Use ! if it really shouldn't become nil in the future, but it needs to be nil initially.

You should go beyond the syntactic sugar.

There are two completely different polymorphic types. The syntactic sugar just uses either one or the other of these types.

When you write Foo? as a type you really have Optional<Foo>, while when you write Foo! you really have ImplicitlyUnwrappedOptional<Foo>.

These are two different types, and they are different from Foo as well.


The values you create with ? are plain optional values as you mentioned, you should access it via optional binding (if let unwrappedValue = myOptionalValue) or by using the exclamation point syntax myOptionalValue!.doSomething().

The values you create with ! are called implicitly unwrapped optionals. With them, you don't need to manually unwrap before using them. When you do val myOptionalValue!.doSomething().

The value will be automatically unwrapped for you when you use myOptionalValue directly, be careful with this though, because accessing an implicitly unwrapped value when there is actually no value in it (when it is nil) will result in a runtime error.


? (Optional) indicates your variable may contain a nil value while
! (unwrapper) indicates your variable must have a memory (or value) when it is used (tried to get a value from it) at runtime.

The main difference is that optional chaining fails gracefully when the optional is nil, whereas forced unwrapping triggers a runtime error when the optional is nil.

To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a nonoptional value. You can use this optional return value to check whether the optional chaining call was successful (the returned optional contains a value), or did not succeed due to a nil value in the chain (the returned optional value is nil).

Specifically, the result of an optional chaining call is of the same type as the expected return value, but wrapped in an optional. A property that normally returns an Int will return an Int? when accessed through optional chaining.

var defaultNil : String?  // declared variable with default nil value
println(defaultNil) >> nil  

var canBeNil : String? = "test"
println(canBeNil) >> optional(test)

canBeNil = nil
println(canBeNil) >> nil

println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper

var canNotBeNil : String! = "test"
print(canNotBeNil) >> "test"


var cantBeNil : String = "test"
cantBeNil = nil // can't do this as it's not optional and show a compile time error

For more detail, refer a document by Apple Developer Commitee, in detail


The String! kind is called an implicitly unwrapped optional:

Sometimes it’s clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it’s useful to remove the need to check and unwrap the optional’s value every time it’s accessed, because it can be safely assumed to have a value all of the time.

These kinds of optionals are defined as implicitly unwrapped optionals. You write an implicitly unwrapped optional by placing an exclamation mark (String!) rather than a question mark (String?) after the type that you want to make optional.


in the optional chaining section you find the answer:

example class:

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

If you try to access the numberOfRooms property of this person’s residence, by placing an exclamation mark after residence to force the unwrapping of its value, you trigger a runtime error, because there is no residence value to unwrap:

let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

The code above succeeds when john.residence has a non-nil value and will set roomCount to an Int value containing the appropriate number of rooms. However, this code always triggers a runtime error when residence is nil, as illustrated above.

Optional chaining provides an alternative way to access the value of numberOfRooms. To use optional chaining, use a question mark in place of the exclamation mark:

if let roomCount = john.residence?.numberOfRooms {
    println("John's residence has \(roomCount) room(s).")
} else {
    println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."