убить и повторно запустить фоновый таймер (DispatchSourceTimer) - PullRequest
0 голосов
/ 02 февраля 2020

На игровом экране я использую фоновую очередь для подсчета прошедшего времени в игре. Это забавное c основано на Даниэле Галаско , идеальное решение для моего приложения: оно позволяет пользователю перемещаться по другим V C, пока таймер еще включен. Иерархия V C довольно проста: VC игровых настроек обрабатываются в tabBarController. Экран игры находится отдельно. Пользователь может изменять настройки при включенном таймере. Настройки хранятся в CoreData.

На моем игровом экране, где мне нужно отобразить таймер, у меня есть метка, отображающая истекшее время и две кнопки: кнопка «Воспроизведение / Пауза» и кнопка «Сброс».

Я называю свой таймер установки забавным c в ViewDidLoad. Значением по умолчанию для моего таймера является сохраненное значение в CoreData, оно было определено в настройках. И это значение увеличивается на 1 каждую секунду, когда таймер включен. У меня также есть состояние c let shared, которое сохраняет статус таймера (возобновлен / приостановлен).

Когда я нахожусь на игровом экране и если мой таймер приостановлен, моя кнопка воспроизведения / паузы работает отлично: Я могу перейти к другим представлениям (то есть отклонить мою экранную игру), снова представить свою экранную игру и возобновить счетчик. Он корректно обновляет мой ярлык.

Проблема в том, что я закрываю игровой экран во время работы таймера. Таймер работает (print fun c показывает, что таймер все еще работает), но когда я снова представляю экран, я не могу приостановить / возобновить / перезапустить его, и моя метка останавливается при втором возвращении ... пока таймер все еще работает.

private var counter: Int16?
var t = RepeatingTimer(timeInterval: 1)
let gameIsOn = isGameOnManager.shared

override func viewDidLoad() {
        super.viewDidLoad()
        print("is timer On ? \(String(describing: gameIsOn.isgameOn))")
        buildTimer()
        if gameIsOn.isgameOn == true {
            resumeTapped = true
            t.resume()
            PlayB.setImage(UIImage(named:"pause"), for: .normal)
        } else {
            resumeTapped = false
        }
    }

    func buildTimer(){
        self.t.eventHandler = {
            self.counter! += 1
            print("counter \(String(describing: self.counter!))")
            self.coreDataEntity?.TimeAttribute = self.counter ?? 0
            self.save()
            DispatchQueue.main.async {
                self.dataField.text = String(describing: self.counter ?? 0)
            }
        }
    }

@objc func didTapButton(_ button: UIButton) {
        if resumeTapped == false {
            t.resume()
            resumeTapped = true
            gameIsOn.isgameOn = true
            PlayB.setImage(UIImage(named:"pause"), for: .normal)
        }
        else if resumeTapped == true {
            t.suspend()
            resumeTapped = false
            gameIsOn.isgameOn = false
            PlayB.setImage(UIImage(named:"play"), for: .normal)
        }
    }

1 Ответ

1 голос
/ 04 февраля 2020

У вас есть сильный цикл ссылок между вашим контроллером представления, таймером и обработчиком событий таймера. Вы должны использовать ссылку weak в замыкании, чтобы разорвать этот цикл:

func buildTimer() {
    t.eventHandler = { [weak self] in
        guard let self = self else { return }

        ...
    }
}

, который фиксирует цикл сильных ссылок.

Но когда вы исправите это, есть вероятность, что вы вы увидите, что ваш таймер перестанет работать, когда вы отключите этот контроллер просмотра. Если это так, это будет означать, что таймер находится не в нужном объекте. Это должен быть объект более высокого уровня, который сохраняется в приложении, а не внутри этого контроллера представления, который представлен и отклонен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...