fuzzygoat
fuzzygoat

Reputation: 26223

Optional chaining and binding

Inside the optional binding when I assign the variable ammo (and ammo2) I am pretty sure that I should be using ! to unbox the optional, but on my first attempt I put ? by mistake and was a little confused why it still worked, can anyone cast some light onto whats going on there?

let soldierA = Soldier(name: "Brian")
soldierA.weapon = Weapon()
soldierA.weapon!.grenadeLauncher = GrenadeLauncher()

let soldierB = Soldier(name: "Gavin")
soldierB.weapon = Weapon()

let soldierC = Soldier(name: "Berty")
soldierC.weapon = Weapon()
soldierC.weapon!.grenadeLauncher = GrenadeLauncher()
soldierC.weapon!.grenadeLauncher!.ammo = 234

let missionTeam = [soldierA, soldierB, soldierC]
for eachSoldier in missionTeam {
    if let launcherAvailable = eachSoldier.weapon?.grenadeLauncher? {
        var ammo =  eachSoldier.weapon!.grenadeLauncher!.ammo // PRETTY SURE THIS IS RIGHT
        var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo // SHOULD THIS WORK, IT DOES?
        println("SOLDIER: \(eachSoldier.name), Weapon has launcher AMMO: \(ammo)")
    } else {
        println("SOLDIER: \(eachSoldier.name), Weapon does not have launcher ")
    }
}

.

// CLASSES
class Soldier {
    var name: String
    var weapon: Weapon?
    init(name: String) {
        self.name = name
    }
}

class Weapon {
    var ammo = 500
    var grenadeLauncher: GrenadeLauncher?
}

class GrenadeLauncher {
    var ammo = 20
}

EDIT

Thank you, I was getting confused about how this works, but I now see what is happening. Here is the modified eachSoldier section again, using optional binding with optional chaining...

for eachSoldier in missionTeam {
    if let weapon = eachSoldier.weapon? {
        if let launcher = eachSoldier.weapon?.grenadeLauncher? {
            println("SOLDIER: \(eachSoldier.name) Weapon has launcher with \(launcher.ammo) ammo")
        } else {
            println("SOLDIER: \(eachSoldier.name) Weapon does not have launcher ")
        }
    } else {
        println("SOLDIER: \(eachSoldier.name) does not have weapon ")
    }
}

Upvotes: 0

Views: 1980

Answers (2)

William Kinaan
William Kinaan

Reputation: 28809

In addition to what @holex has stated, I would like to say that your case called Optional Chaining, in which if you use ? instead of ! on an optional variable (or constant), that means you are checking if the variable (or the constant) is not nil. In other words, it has a value.

The lovely thing about optional chaining is that you can apply it to many levels. For example:

Let's say you have these two classes:

class Student{
    var subjects: [Subject]?
}
class Subject{
    var name: String?
}

and you created a variable:

var william = Student()

At any time, you can print the name of the first subject as this:

print(william.subjects?[0].name)

Notice that the result of that print statement is nil, while if you unwrapped it like this:

print(william.subjects![0].name)

You would get a run time error

Upvotes: 0

holex
holex

Reputation: 24031

soldierC.weapon = Weapon()
soldierC.weapon!.grenadeLauncher = GrenadeLauncher()
soldierC.weapon!.grenadeLauncher!.ammo = 234

it is correct in the current pattern.


var ammo =  eachSoldier.weapon!.grenadeLauncher!.ammo

implicitly unwraps the weapon and its grenadeLauncher; it does not care of whether or not they have been inited before, therefore it could lead a direct crash if your code tries to unwrap when any of them is still a nil value.


var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo

tries to access the weapon and its grenadeLauncher; if the object does not exist, they will be left alone, therefore nothing happens but the ammo2 will be nil only, and application can proceed.

therefore your flow could be similar to that:

for eachSoldier in missionTeam {
    var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo
    if ammo2 != nil {
        println("SOLDIER: \(eachSoldier.name), Weapon has launcher AMMO: \(ammo2)")
    } else {
        println("SOLDIER: \(eachSoldier.name), Weapon does not have launcher ")
    }
}

Upvotes: 3

Related Questions