3

I have a Car class. Let's say a car goes to the junkyard, this car should no longer be counted in the total population. I have the deinit function, but how do I systematically remove a car from the car population? In other words, how do I get the deinit to take effect?

I have a class variable isJunk but don't know how to use it to make this work.

class Car {
    static var population: Int = 0
    var isJunk: Bool = false
    var color: String
    var capacity: Int
    var driver: Bool?
    var carOn: Bool = false
    init (carColor: String, carCapacity: Int) {
        self.capacity = carCapacity
        self.color = carColor
        Car.population += 1

    }
    deinit {
        Car.population -= 1
    }

    func startCar() {
        self.carOn = true
    }
}
Govind Rai
  • 14,406
  • 9
  • 72
  • 83
  • 2
    Isjunk won't do anything. Once the object is deinitialized, its pointer is lost and you can no longer use it. The easiest way to send an object to the garbage collection is by just setting every reference to that object to nil. Automatic reference counting will do the rest. – Philip Feldmann Jun 24 '16 at 21:26
  • 2
    @Philip Feldmann: i think, there's no **garbage collection** in Xcode. just ARC – Ulli H Jun 24 '16 at 21:42
  • You're correct there is just ARC, however when all references are nil, ARC will dispose of the object and deinit will be called. If it's not called you have a leak. – Feldur Jun 25 '16 at 07:14
  • @Feldur Which is also the case under GC. GC isn't some magic silver bullet that predicts what you won't need anymore and frees it from under your nose. It requires you to dispose of references to objects you no longer want. It'll only clean up those objects which have no reference. If you keep the reference but never actually use it, it's a leak, whether GC, ARC or manual management. – Alexander Sep 12 '19 at 18:31
  • @Alexander Of course. That's been true since before I first modified a Lisp interpreter many decades ago, and it will remain true for decades to come. GC technology has evolved a lot from that early mark-and-sweep system, but it's still just an algorithm. – Feldur Sep 12 '19 at 23:21

2 Answers2

5
class Car {
    static var population: Int = 0
    init() {
        Car.population += 1

    }
    deinit {
        Car.population -= 1
    }
}

var cars: [Car] = [Car(), Car()]
print("Population:", Car.population) // "Population: 2"

// now the second car is removed from array and we have no other references to it
// it gets removed from memory and deinit is called
cars.removeLast()
print("Population:", Car.population) // "Population: 1"

However, the same can be achieved just by asking the number of items in cars array. And that's usually a better alternative than a private counter of instances.

To keep the items in memory you will always need some kind of register (e.g. an array) for them. And that register can keep them counted.

One possibility:

class CarPopulation {
    var liveCars: [Car] = []
    var junkCars: [Car] = []
}

Or you can keep them in one array and set junk on the car and count non-junk cars when needed:

class CarPopulation {
    var cars: [Car] = []

    func liveCars() -> Int {
        return self.cars.filter { !$0.junk }.count
    }
}

There are many possibilities but extracting the counters to some other class that owns the cars is probably a better solution.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • I understand! Could you provide some insight into say if instead of an array it was `var car = Car()` and `var car2 = Car()`. How would you eliminate the second reference? – Govind Rai Jun 24 '16 at 21:35
  • You can delete the reference by doing `car2 = nil`, if this is the only reference to the object behind car2. – Philip Feldmann Jun 24 '16 at 21:36
  • However, note that `deinit` does not have to be called immediately. – Sulthan Jun 24 '16 at 21:38
  • @PhilipFeldmann maybe I am not understanding your comment correct but when I set car2 to nil I get a `error: nil cannot be assigned to type 'Car'`. Not sure what you mean by "if this is the only reference to the object behind car2" – Govind Rai Jun 24 '16 at 21:43
  • 2
    @GovindRai In swift, you can't set variables that are not optional to nil, so your variable has to be optional. When you initialize an object, the OS will make room in your memory for that object. You can have multiple pointers to the same object(memory address), for example `car3 = car2` will not create a copy of car2, it'll only create a new reference. You have to delete every single reference of an object in order to make the GC call its deinit method. However, like Sulthan mentioned already, it's a better practice to let another class handle the counting. – Philip Feldmann Jun 24 '16 at 21:52
  • "However, note that deinit does not have to be called immediately." Don't understand that comment - I thought a supposed advantage of ARC relative to garbage collection was that it is entirely deterministic. – RenniePet Mar 12 '17 at 14:45
  • @RenniePet It's deterministic but some situations require the system to postpone the deallocation (e.g. until the current method exits). ARC also operates with a lot of non-ARC code which means that autorelease pools are heavily used, postponing the deallocation until the given pool is drained. For temporary objects we can be usually sure that an object is deallocated immediately. The determinism is always there though. – Sulthan Mar 12 '17 at 15:00
  • OK, thanks. But for what it's worth my opinion is that garbage collection is much better than ARC. I now live in constant fear of creating memory leaks. When working with C# and Java I can sleep at night. – RenniePet Mar 12 '17 at 15:21
  • @RenniePet Different tools for different tasks. After years of experience with ARC I can say that if you keep your object hierarchy simple and clear, there is no risk of leaks. Most leaks can also be detected by Instruments. GC needs lots of memory and destroys performance. I have spent a lot of my days working on IDEs built on Java and I still have nightmares about increasing heap size and waiting till GC does its job. – Sulthan Mar 12 '17 at 15:52
  • @RenniePet Nothing discussed here has had any risk of creating a memory leak, neither under ARC nor under GC. ARC only leaks memory with strong reference cycles. GC basically always leak some memory, short term, until the next clean up. I suggest you check out https://sealedabstract.com/rants/why-mobile-web-apps-are-slow/ , it's a fascinating read. – Alexander Sep 12 '19 at 18:29
1

The deinit is called when you deallocate your instance of Car (when you entirely get rid of the instance of the object). When you put a Car instance in the junkyard I don't think you want to get rid of the instance of Car, you really just want to change its location. I would suggest a different function to handle changing the location of Car.

Perhaps:

func changeLocation(newLocation: String) {
   // Perhaps add an instance variable to 'remember' the location of the car
   switch newLocation {
   case "junkyard":
     Car.population -= 1
   default:
      // Perhaps check whether previous location was Junkyard and increment  
      // counter if the Car is coming out of the Junkyard
      print("Unrecognized location")
   }

}
user212514
  • 3,110
  • 1
  • 15
  • 11