I have a main actor isolated class like this one:
@MainActor
open class TimerScheduler {
private enum TimerState {
case normal
case paused(remaining: TimeInterval)
}
private struct TimerInfo {
let timer: Timer
let state: TimerState
}
private var keyToTimerInfoMap = [String:TimerInfo]()
private let lifecycleChecker: () -> Bool
public init(lifecycleChecker: @escaping @MainActor () -> Bool = { true }) {
self.lifecycleChecker = lifecycleChecker
// Note: When app is in background, NSTimer will only get a few minutes of execution.
// Then the timer will be paused
// These notifications are there to ensure NSTimer is paused immediately when app goes background.
NotificationCenter.default.addObserver(self, selector: #selector(pause), name:UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(resume), name:UIApplication.didBecomeActiveNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
for (_, info) in keyToTimerInfoMap {
info.timer.invalidate()
}
}
}
I have a warning:
Cannot access property ‘keyToTimerInfoMap’ with a non-sendable type ‘[String : TimerScheduler.TimerInfo]’ from non-isolated deinit; this is an error in the Swift 6 language mode
With a suggestion to convert TimerInfo
Sendable, which I can’t do because Timer
is not Sendable.
I could wrap the deinit
in a main actor Task like this:
deinit {
NotificationCenter.default.removeObserver(self)
Task { @MainActor in
for (_, info) in keyToTimerInfoMap {
info.timer.invalidate()
}
}
}
However, if the object is deinit
‘ed in the background thread, I would be using the object (accessing its keyToTimerInfoMap
field) after it’s been deallocated, which seems to be a dangerous thing to do.