2

I'm a beginner in Swift and static programming. Going through Big Nerd Ranch Swift book. What I don't understand is why myTown population is changing, but not fredTheZombie.town? The exercise calls for the town population to never go below zero. This could happen when the population is less than 10. How do I change the fredTheZombie.town population variable?

struct Town {
    var population = 5422
    var numberOfStoplights = 4

    func printTownDescription() {
        print("Population: \(myTown.population), number of stoplights: \(myTown.numberOfStoplights)")
    }

    mutating func changePopulation(amount: Int) {
        population += amount
    }
}

class Monster {
    var town: Town?
    var name = "Monster"

    func terrorizeTown() {
        if town != nil {
            print("\(name) is terrorizing a town!")
        } else {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }
}

class Zombie: Monster {
    var walksWithLimp = true

    final override func terrorizeTown() {
        guard town?.population > 0 else {
            return
        }

        if town?.population > 10 {
            super.town!.changePopulation(-10)
        } else {
            super.town!.population = 0
        }

        super.terrorizeTown()
    }

    func changeName(name: String, walksWithLimp: Bool) {
        self.name = name
        self.walksWithLimp = walksWithLimp
    }
}

var myTown = Town()
myTown.changePopulation(500)
let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown()
fredTheZombie.town?.printTownDescription()
fredTheZombie.changeName("Fred the Zombie", walksWithLimp: false)

myTown.changePopulation(-5915)
print(myTown.population)
print(fredTheZombie.town!.population)
fredTheZombie.terrorizeTown()
fredTheZombie.terrorizeTown()
fredTheZombie.town?.printTownDescription()

Output:

Monster is terrorizing a town!
Population: 5922, number of stoplights: 4
7
5912
Fred the Zombie is terrorizing a town!
Fred the Zombie is terrorizing a town!
Population: 7, number of stoplights: 4
Program ended with exit code: 0

1 Answer 1

2

Town is a struct, a value type. When assigning a value type to a different variable (e.g. fredTheZombie.town = myTown), that value type is copied (meaning that a new Town is created with the same values as the original Town).

myTown and fredTheZombie.town are not the same town any more. If you want them to be the same instance, make them a class (a reference type).

See more info on Apple Swift Blog and in Swift Language Guide.

The biggest problem we have are optional structs. The result of unwrapping an optional struct (using town!) is again a copy, a new town. To work around that, you have to:

var myTown = super.town!
myTown.changePopulation(-10)
super.town = myTown // assign back

Whole example:

struct Town {
    var population = 5422
    var numberOfStoplights = 4

    func printTownDescription() {
        // note you have to print "self" here, not "myTown"
        print("Population: \(self.population), number of stoplights: \(self.numberOfStoplights)")
    }

    mutating func changePopulation(amount: Int) {
        population += amount
    }
}

class Monster {
    var town: Town?
    var name = "Monster"

    func terrorizeTown() {
        if town != nil {
            print("\(name) is terrorizing a town!")
        } else {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }
}

class Zombie: Monster {
    var walksWithLimp = true

    final override func terrorizeTown() {
        guard super.town?.population > 0 else {
            return
        }

        var town = super.town!

        if town.population > 10 {
            town.changePopulation(-10)
        } else {
            town.population = 0
        }

        super.town = town

        super.terrorizeTown()
    }

    func changeName(name: String, walksWithLimp: Bool) {
        self.name = name
        self.walksWithLimp = walksWithLimp
    }
}

In your case there is no need for town to be actually optional. You could just pass it to Monster in a constructor.

Sign up to request clarification or add additional context in comments.

7 Comments

That makes sense. Is there a way to change fredTheZombie.town without turning it into the same instance (myTown)?
@ltrainpr You can assign to any of its variables or call a mutating method. However, since fredTheZombie.town is an optional, it won't really work because the result of unwrapping is again a copy. The most common way to work with value types is to always create a new instance.
@ltrainpr Oh, I see your problem now, let me edit the answer.
Why super.town = myTown instead of fredTheZombie.town = myTown or will it work either way? Or wait, does that code go in the Zombie Class?
@ltrainpr Depends on the context where you are calling that. You cannot use super.town outside a method body and you cannot use fredTheZombie.town from func terrorizeTown.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.