1

I'm following the suggestions given in this answer. Still my timer is sometimes not nil

func recreateTimer(){

        let timerLog = OSLog(subsystem: "LocationLogger", category: "Timer")

        guard shouldUseTimer == true else {
            os_log("we are using CoreMotion, no need to set timer", log: timerLog, type: .error) 
            return
        }
        if dwellingTimer != nil{

            dwellingTimer?.invalidate()
            dwellingTimer = nil
        }

        lastDwelling = lastLocation

        os_log("Moved more than 160 | invalidated previous timer began new timer", log: timerLog, type: .error)

        DispatchQueue.main.async { [weak self] in

            guard self?.dwellingTimer == nil else{
                os_log("Timer was not niled!!!", log: timerLog, type: .error)
                return
            }
            self?.dwellingTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { timer in
                os_log("we reached 1 minute of no movement",log: timerLog, type: .fault)
                self?.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
                self?.beginMotionTracking()
                os_log("Timer is deallocated & invalidated | Accuracy: %{public}f | RemainingTime: %{public}f",log: timerLog, type: .fault,(self?.locationManager.desiredAccuracy)!, UIApplication.shared.remainingTime())
            })
        }
        os_log("New Timer is 'likely' set now", log: timerLog, type: .error)
    }

Sometimes I get hits on

os_log("we are using CoreMotion, no need to set timer", log: timerLog, type: .error).
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • Is it possible that spawning the timer in a dispatchq block is the issue? – solenoid Aug 08 '17 at 20:15
  • 1
    You should dispatch all updates to your `dwellingTimer` property synchronously on a serial dispatch queue to ensure that the operations are thread-safe – Paulw11 Aug 08 '17 at 20:39
  • @Paulw11 I'll definitely do that. I guess what I need to do is to either create a serial queue and add the timer to main runloop or just put the invalidation and `nil`ling inside the `DispatchQueue.main.async`... However what I don't understand is how could this be a problem from the first place... the main thread is a serial queue, and also the `if dwellingTimer != nil{... ` line is also called in a serial manner. So each part should be OK... – mfaani Aug 08 '17 at 23:31
  • @Paulw11 Actually let's imagine I get 3 super fast calls, niled, niled, niled, scheduled, scheduled, scheduled (ie I won't get niled, scheduled, niled, scheduled, niled scheduled) --> so I get 3 timers rather than 1?! Hmmm If that's what you meant then even `dispatch.main.async` isn't going to help, because it's not `synchronously` as you said and I must do it manually ie create thread add timer to runloop...Please correct me where ever I was wrong – mfaani Aug 08 '17 at 23:37
  • Yes, access to properties may not be atomic and async dispatch is going to happen some time after the current function returns. If you update the property (and schedule the timer if necessary) in atomic operations (via dispatch_sync if required) then you will guard against this – Paulw11 Aug 08 '17 at 23:39
  • @Paulw11 Just to be sure, the current way I was doing ie using dispatchqueue.main.async was *running* in the background state. If I don't change anything...would creating a background thread dispatch as expected when app is in background State – mfaani Aug 08 '17 at 23:56
  • You can still dispatch synchronously on a private queue in the background. – Paulw11 Aug 08 '17 at 23:57
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151527/discussion-between-honey-and-paulw11). – mfaani Aug 09 '17 at 18:24
  • Perhaps set breakpoints; maybe you are scheduling more than one timer. – Paulw11 Aug 10 '17 at 12:45

1 Answers1

0

Thanks to solenoid and Paulw11 comments, I learned that all executions related to a timer should be done in a serial manner.

Also see invalidate

You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

So in addition to invalidating and nilling (you don't want to give non-nil for an object that no longer conveys any meaning), make sure that you:

  1. use the same thread
  2. the thread you are using is serial
  3. depending on where you make changes onto the timer you may or may not need synchronization.
mfaani
  • 33,269
  • 19
  • 164
  • 293