Compiler choosing the wrong initializer - swift

I have an EXC_BAD_ACCESS(code=2 ...) error that keeps popping up that I need some help with. I believe I've managed to pin down the source of the malformed pointer, but I'm at a loss as to how to fix it.
Apparently the swift compiler is choosing the wrong initializer for one of my classes. According to Instruments, sometimes when the class Description is initialized, it calls the initializer for LyricBlock. Not all the time, just sometimes. It does this regardless of whether the compiler is set to -Onone or -O whole-module-optimization.
Here's what the two classes look like:
class LyricBlock: Node, LeftDelimited, RightDelimited {
var leftDelimiter: Delimiter
var rigthDelimiter: Delimiter
init(start: Int, body: Range<Int>, end: Int) {
self.leftDelimiter = Delimiter(range: start..<body.lowerBound)
self.rightDelimiter = Delimiter(range: body.upperBound..<end)
super.init(range: body)
}
}
class Description: Node, LeftDelimited, RightDelimited {
var leftDelimiter: Delimiter
var leftDelimiter: Delimiter
init(start: Int, body: Range<Int>, end: Int) {
self.leftDelimiter = Delimiter(range: start..<body.lowerBound)
self.rightDelimiter = Delimiter(range: body.upperBound..<end)
super.init(range: body)
}
}
As you can see, LyricBlock and Description inherit from a Node base class and share a couple protocols in common, but otherwise they have nothing to do with each other.
Some possibly relevant code:
class Node {
weak var parent: Node?
var next: Node?
var firstChild: Node?
weak var lastChild: Node?
let offset: Int
internal(set) var length: Int
init(range: Range<Int>) {
self.offset = range.lowerBound
self.length = range.upperBound - range.lowerBound
}
func addChild(_ child: Node) {
if firstChild == nil {
firstChild = child
} else {
lastChild?.next = child
}
lastChild = child
child.parent = self
}
}
class Parser {
// ...
func processLine(in buffer: Buffer) {
// Parse the current line as a block node.
var block = blockForLine(in: buffer)
// Try to find an appropriate container node. If none can be found, block will be replaced with Description.
let container = appropriateContainer(for: &block, in: buffer)
container.addChild(block)
// Edge case to parse first-line lyrics
if let cueBlock = block as? CueBlock {
if let lyricBlock = scanForLyric(in: buffer, at: cueBlock.direction.range.lowerBound) {
let lyricContainer = LyricContainer(range: lyricBlock.range.lowerBound..<endOfLineCharNumber)
lyricContainer.addChild(lyricBlock)
cueBlock.replaceDirection(with: lyricContainer)
parseInlines(for: lyricBlock, in: buffer)
}
}
// Parse inlines as appropriate
switch block {
case is FacsimileBlock, is Description, is LyricBlock:
parseInlines(for: block, in: buffer)
// ...
}
}
func blockForLine(in buffer: Buffer) -> Node {
let whitespace = buffer.scanForFirstNonspace(at: charNumber, limit: endOfLineCharNumber)
// ...
let endWhitespace = buffer.scanBackwardForFirstNonspace(at: endOfLineCharNumber, limit: wc)
let description = Description(start: charNumber, body: whitespace..< endWhitespace, end: endOfLineCharNumber)
return description
}
func appropriateContainer(for block: inout Node, in buffer: Buffer) -> Node {
switch block {
// These block types can only ever be level-1
case is Header, is Description, is EndBlock, is HorizontalBreak:
return root
// ...
case is LyricBlock:
guard let cueContainer = root.lastChild as? CueContainer else { break }
guard let cueBlock = cueContainer.lastChild as? CueBlock else { break }
guard let direction = cueBlock.direction as? LyricContainer else { break }
direction.extendLengthToInclude(node: block)
cueBlock.extendLengthToInclude(node: direction)
cueContainer.extendLengthToInclude(node: cueBlock)
return direction
default:
break
}
let whitespace = buffer.scanForFirstNonspace(at: charNumber, limit: endOfLineCharNumber)
let endWhitespace = buffer.scanBackwardForFirstNonspace(at: endOfLineCharNumber, limit: wc)
// Invalid syntax, time to fail gracefully
block = Description(start: charNumber, body: whitespace..< endWhitespace, end: endOfLineCharNumber)
return root
}
func parseInlines(for stream: Node, in buffer: Buffer) {
// ... scans buffer for inlines and enques them in queue
while let next = queue.dequeue() {
let nextRange = next.rangeIncludingMarkers
if nextRange.lowerBound > j {
let lit = Literal(range: j..<nextRange.lowerBound)
stream.addChild(lit)
}
stream.addChild(next)
j = nextRange.upperBound
}
if j < endOfLineCharNumber {
let lit = Literal(range: j..<nodeRange.upperBound)
stream.addChild(lit)
}
}
// ...
}
As a side note, I wondered if I might be running into a mangling issue with the class signatures and tried making rightDelimiter and leftDelimiter properties of Node instead of using protocols. This resulted in the compiler calling my Identifier initializer instead. I don't know what that proves. Frankly I'm at a loss. Help?

After some more tinkering I was able to at least partially solve this problem.
It turns out that, for whatever reason, ARC craps out on linked lists above a certain size. I was able to confirm this by creating a generic linked list in a new project and slowly stress testing it with progressively longer lists.
class LLNode<T> {
var value: T?
var next: LLNode<T>?
weak var previous: LLNode<T>?
}
class LList<T> {
var head: LLNode<T>
var tail: LLNode<T>
// boring old linked list implementation
}
let list = List<Int>()
for i in 0..<10000 {
list.append(i)
}
Sure enough, after about 10,000 nodes I started getting EXC_BAD_ACCESS again on the generic list, but only when list was deinitialized. This matched the behavior I was getting exactly with the parser above. Since my code uses linked lists to model children in a tree (two dimensions of reference counting!), ARC was having to resolve all those references on its own -- and failing. Why it crashes like that is still beyond my ability to explain, but that at least explains the source of the crash.
To confirm, I created another linked list in C and just made a Swift wrapper around it, with all of the garbage collection being implemented in C.
import liblist
class List<T> {
var list: UnsafeMutablePointer<llist>
// Swift interface with list
deinit {
list_free(list)
}
}
The wrapper was able to handle every size I threw at it -- as much as 500,000 nodes, at which point I felt satisfied and stopped testing.
If anyone is interested in the full code I used to get around this problem, I've created a Swift package at https://github.com/dmcarth/List

Related

Minimum Window Substring in Swift

I am trying to learn swift by solving interview questions. One of the question that I am trying to solve is as follows.
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
My implementation is as follows which holds t string characters and its corresponding index retrieved from s.
func minimumWindowSubstring(_ s: String, _ t: String) -> String{
let sChar = [Character](s)
let tChar = [Character](t)
var indexTemp = [[Character:Int]()]
for tc in tChar
{
for (j, sc) in sChar.enumerated()
{
if sc == tc
{
indexTemp.append([tc:j])
}
}
}
return ""
}
what I have in indexTemp array is as follows
Now I wonder how could I able to use this array to find the minimumwindow, I stuck.
I thought it was an interesting problem so I gave it a shot. Instead of using a dictionary I used a simple class to store the range of characters found, as well as a String that stores which characters haven't been found.
It only goes through the main string once, so it should be O(n).
You can run this in the playground.
(I know you wanted help in fixing your code and my answer doesn't do that, but I'm hoping it will provide enough insight for you to adjust your own code)
import Foundation
let string = "ADOBECODEBANC"
let sub = "ABC"
// Create a class to hold the start and end index of the current search range, as well as a remaining variable
// that will store which characters from sub haven't been found
class Container {
var start: Int
var end: Int?
var remaining: String
// Consume will attempt to find a given character within our remaining string, if it has found all of them,
// it will store the end index
func consume(character: Character, at index: Int) {
// If remaining is empty, we're done
guard remaining != "" else { return }
// We're assuming that sub won't have repeating characters. If it does we'll have to chage this
remaining = remaining.replacingOccurrences(of: String(character), with: "")
if remaining == "" {
end = index
}
}
init(start: Int, remaining: String) {
self.start = start
self.remaining = remaining
}
}
// ClosedContainer is similar to Container, but it can only be initialized by an existing container. If the existing
// container doesn't have an end value, the initialization will fail and return nil. This way we can weed out containers
// for ranges where we didn't find all characters.
class ClosedContainer {
let start: Int
let end: Int
init?(container: Container) {
guard let end = container.end else { return nil }
self.start = container.start
self.end = end
}
var length: Int {
return end - start
}
}
var containers = [Container]()
// Go through each character of the string
string.enumerated().forEach { index, character in
// Look for matches in sub
if sub.contains(character) {
// Allow each existing container to attempt to consume the character
containers.forEach { container in
container.consume(character: character, at: index)
}
// Create a new container starting on this index. It's remaining value will be the sub string without the
// character we just found
let container = Container(start: index, remaining: sub.replacingOccurrences(of: String(character), with: ""))
containers.append(container)
}
}
// Convert Containers into ClosedContainers using compactMap, then find the one with the shortest length
let closedContainers = containers.compactMap(ClosedContainer.init)
let maybeShortest = closedContainers.min { $0.length < $1.length }
if let shortest = maybeShortest {
// Convert int to String indices
let start = string.index(string.startIndex, offsetBy: shortest.start)
let end = string.index(string.startIndex, offsetBy: shortest.end)
// Get the result string
let result = string[start...end]
print("Shortest substring of", string, "that contains", sub, "is", result)
} else {
// No range was found that had all characters in sub
print(string, "doesn't contain all characters in", sub)
}

Swift optional binding in while loop

According to Swift documentation:
Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
The documentation only shows an example of optional-binding using if statement like:
if let constantName = someOptional {
statements
}
I'm looking for an example of optional-binding using while loop?
It's the same
while let someValue = someOptional
{
doSomethingThatmightAffectSomeOptional(with: someValue)
}
Here is a concrete example of iterating a linked list.
class ListNode
{
var value: String
var next: ListNode?
init(_ value: String, _ tail: ListNode?)
{
self.value = value
self.next = tail
}
}
let list = ListNode("foo", ListNode("bar", nil))
var currentNode: ListNode? = list
while let thisNode = currentNode
{
print(thisNode.value)
currentNode = thisNode.next
}
// prints foo and then bar and then stops
I think the advantage of using while let ... is infinite loop that check some variable for any changes. But it's weird. For this kind of work you should use didSet. Or other good example is List data structure. Anyway, here is the example:
var value: Int? = 2
while let value = value {
print(value) // 2
break
}

receiving right type in function

I try to build some NestedView, which loads view inside view inside... depends of objects tree. So I made a short test in Playground:
class First {}
class Second:First {}
class Dodd {
func takeIt<Foo, Bar>(content:[Foo], type: Bar.Type) {
print (Foo.self, Bar.self)
print (content as? [Bar] ?? [])
}
}
let arrayOfFirst:[First] = [First()]
let arrayOfSecond:[Second] = [Second(), Second(), Second()]
let dodd = Dodd()
let firstType = First.self
let secondType = Second.self
dodd.takeIt(content: arrayOfSecond, type: firstType)
dodd.takeIt(content: arrayOfFirst, type: secondType)
It produces nice and predictable output:
Second First
[__lldb_expr_77.Second, __lldb_expr_77.Second, __lldb_expr_77.Second]
First Second
[]
Great.
But if I try tu use exactly same mechanism in a bit more complicated environment results are less satisfying.
This is a function of some ViewController, whatever:
func addSubviews<HeadType>(for content:[HeadType]) {
Swift.print("ADD SUBVIEW FOR \(HeadType.self)")
func takeNext <ParentType, ChildType>(
parentArray: [ParentType],
pathToChild: AnyKeyPath,
type: ChildType.Type?) -> [ChildType]
{
Swift.print ("\nInside takeNext Child Type <\(ChildType.self)>\n")
let result:[ChildType] = parentArray.flatMap { ( parentElement ) -> ChildType? in
parentElement [keyPath:pathToChild] as? ChildType}
return result
}
let interfaceHead = InterfaceElements(type: HeadType.self)
Swift.print ("\tParentArrayContent: \(HeadType.self) ")
for (label, path) in interfaceHead.paths {
if let firstObject = content.first, let objectAtKeyPath = firstObject[ keyPath:path ] {
Swift.print ("\t\tpath: \(path)")
Swift.print ("\t\ttype: \(InterfaceElements(element: objectAtKeyPath).type.self)")
Swift.print("\t\tLabel", label)
let childType = InterfaceElements(element: objectAtKeyPath).type
let elements = takeNext(
parentArray: content,
pathToChild: path,
type: childType)
let controller = NestedStackViewController(for: elements)
}
}
}
Output:
ADD SUBVIEW FOR Joint
ParentArrayContent: Joint
path: Swift.ReferenceWritableKeyPath<HyperGlyph.Joint, Swift.ImplicitlyUnwrappedOptional<HyperGlyph.Position>>
type: Optional(HyperGlyph.Position)
Label position
Inside takeNext Child Type <NSManagedObject>
ADD SUBVIEW FOR NSManagedObject
ParentArrayContent: NSManagedObject
inited NestedStack for NSManagedObject
inited NestedStack for Joint
First loop is nice, Joint is Joint, Position type was found, sent to takeNext, but inside takeNext function Position became NSManagedObject type. Where could be trick?
Only one way to do it was to explicitly pass a type.
let elements = takeNext(
parentArray: content,
pathToChild: path,
type: SomeType.self) // << Here.
It made a program a bit more complicated, more switches, but it works in nice strong-type way now.

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.

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 ")
}
}
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 ")
}
}
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