3

As everyone knows when using the awesomeness that is Firebase in iOS,

whenever you have observations in a view controller,,

var o: DatabaseReference?
var o2: DatabaseReference?
var o3: DatabaseReference?

it's essential that when that screen goes away, you must terminate all the observations...

private func clearObservations() {

    print("\n\n clearing observations! \n\n")

    if o != nil {

        o?.removeAllObservers()
        o = nil
    }

    if o2 != nil {
    etc...
}

However!

After considerable testing, you cannot call clearObservations() in deinit - you must call it in viewDidDisappear (or some other logical place).

Again - it explicitly does not work in deinit.

My question, why would it be that actually this does not work in deinit?


BTW, you fire up a Firebase observor like this:

say, viewWillAppear#

    o = Database.database().reference(withPath: "string")
    o!.observe(.value, with: { (snapshot) in

        self.blah(snapshot)
    })
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • not sure about it just a guess, but at a time when deinit is called memory is deallocated, so calling something within it won't work, as nothing points to memory any more.Or as Rob mentioned if deinit itself is not called, then check for retain cycle. – Tushar Sharma Dec 08 '17 at 19:43
  • 1
    Why do you say “cannot call `clearObservers()` in `deinit`”? Is it that it crashes or is it because `deinit` is not called? Standard observers are removed in `deinit`. (Or the new Swift 4 observer pattern obsoletes this concern entirely.) If it’s that `deinit` is not called, there might be something in your Firebase observers (any closures involved?) that is causing strong reference cycle, and rather than relying on `viewDidDisappear` pattern, maybe you should just fix root strong reference cycle problem. Maybe show us how you created one of these Firebase observers. – Rob Dec 08 '17 at 19:45
  • hi @Rob : we tested it (incredibly extensively, on a vast array of devices/os) and it does not actually clear the Firebase observors, they keep on observing the hell out of the cloud (there's no crash) – Fattie Dec 08 '17 at 20:20
  • just FYI "Standard observers are removed in deinit" that's really interesting; didn't know it - note that Firebase "observors" have absolutely no connection whatsoever to Swift observors, it's just a coincidence of naming. :O – Fattie Dec 08 '17 at 20:22
  • 1
    @Fattie - So the question was whether your `deinit` is getting called. I’m wagering it’s not, most likely because your closures are not employing the `[weak self]` or `[unowned self]` pattern. I’m guessing a simple strong reference cycle because of these closures, and, assuming all of this is true (I’m only guessing given that you haven’t shared example of how observer was added), it’s probably best to eliminate these strong reference cycles rather than programming around them. – Rob Dec 08 '17 at 20:45
  • hi @Rob , that's very astute, but the thing is, even if you weak self in the observor callback (notice my edit), the actual firebase cloud-system just **keeps the hell on operating** until you absolutely explicitly call "removeAllObservers()" on that handle. (As you can imagine, a really basic mistake with new-chum FBase programmers in iOS is they just create the FBase observor, and *don't bother keeping it* - result, you can never, ever tell the app to stop communicating w/ the cloud on that one!) – Fattie Dec 08 '17 at 21:25
  • (the innocent ".observe(" call to the FBase iOS library, launches a whole thingy that runs and interacts w/ the internet ... much as if you, say, launch a server or something, it makes no diff. at all if the instance which happened to launch it goes away/etc.) – Fattie Dec 08 '17 at 21:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160825/discussion-between-rob-and-fattie). – Rob Dec 08 '17 at 22:07
  • I agree with Josh Hormann and posted answer [here](https://stackoverflow.com/a/51884273/6392347). – Daniel Aug 16 '18 at 19:43

1 Answers1

3

I believe your issue is likely that deinit is not being called at all and usually this kind of thing is because your observer closure is strongly retaining self and the closure is itself retained by the firebase service. That means your object can never die. You can break this kind of cycle with an unowned or weak capture.

To check if this is the case, put a print in your deinit. If the print does not get called when you expect then the object is not being released and you should run the memory debugger to see who else is pointing at it.

I've had the same issue before when forgetting to put unowned on a realm observe method.

Josh Homann
  • 15,933
  • 3
  • 30
  • 33
  • Josh - good points; another confusing thing is you _want_ to keep the reference _to_ the firebase observation process, because you have to explicitly tell that handle to "stop the process". nil'ing a Firebase DatabaseReference (or having it go away if you go away) does NOT stop the observation. https://stackoverflow.com/questions/47320099/is-it-absolutely-correct-that-niling-a-firebase-databasereference-does-not-stop – Fattie Dec 09 '17 at 16:10