Reputation: 26223
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
}
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
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
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