Эффективное и стабильное время в Swift (GCD)

Фитнес-приложение, над которым я работаю для развлечения, требует, чтобы данные о движении добавлялись в массив каждые 0,1 секунды, однако процессы энергосбережения, происходящие, когда приложение находится в фоновом режиме или устройство заблокировано, означает, что эта точность дрейфует до секунды.

Я понимаю, что традиционный таймер часто следует описанному поведению, и поэтому исследую альтернативные методы, в частности таймеры GCD. Учебник, написанный на Medium объясняет один такой метод, однако я не могу успешно выполнить функцию (ы).

Я бегаю:

let t = RepeatingTimer(timeInterval: 0.1)
    t.eventHandler = {
        print("Timer Fired")
    }
    t.resume()

в моей функции viewDidLoad, но безрезультатно - текст не печатается.

override func viewDidLoad() {}

Мой полный код выглядит следующим образом:

import Foundation
import UIKit
import SystemConfiguration

class accelVController: UIViewController {
    override func viewDidLoad() {
       let t = RepeatingTimer(timeInterval: 0.1)
       t.eventHandler = {
          print("Timer Fired")
       }
       t.resume()
   }
}



/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
/// crashes that occur from calling resume multiple times on a timer that is
/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
class RepeatingTimer {

    let timeInterval: TimeInterval

    init(timeInterval: TimeInterval) {
        self.timeInterval = timeInterval
    }

    private lazy var timer: DispatchSourceTimer = {
        let t = DispatchSource.makeTimerSource()
        t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
        t.setEventHandler(handler: { [weak self] in
            self?.eventHandler?()
        })
        return t
    }()

    var eventHandler: (() -> Void)?

    private enum State {
        case suspended
        case resumed
    }

    private var state: State = .suspended

    deinit {
        timer.setEventHandler {}
        timer.cancel()
        /*
         If the timer is suspended, calling cancel without resuming
         triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
         */
        resume()
        eventHandler = nil
    }

    func resume() {
        if state == .resumed {
            return
        }
        state = .resumed
        timer.resume()
    }

    func suspend() {
        if state == .suspended {
            return
        }
        state = .suspended
        timer.suspend()
    }

} 

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


Обновлять: Приложение в основном основано на обнаружении падения, хотя с ним связаны элементы местоположения.

Он основан на трех массивах.

Массив а: содержит метку времени, а также информацию о том, является ли ускорение ниже минимального порога (1), больше ли оно, чем максимальное пороговое значение (2), или нет (0). По сути, это регистр, и 2D - [[отметка времени, статус]]

Массив b: является результатом цикла массив а и поиска значений состояния, равных 1. Если это обнаружено, метка времени элемента (только) добавляется к массиву (от а до б).

Массив c: значения добавляются, когда функция, которая проверяет, содержит ли период времени 2 секунды 1, за которой следует 2 (из массив b). Это рассматривается как возможное падение. Добавляется только временная метка.

Затем массив c просматривается в цикле и исследуется на периоды бездействия после падения в течение 3 секунд, что указывает на бессознательное состояние и необходимость послать за помощью.

Я понимаю, что этот метод может быть неэффективным, и это не лучший способ сделать это, однако, имея мало знаний о Swift, он показался мне наиболее очевидным и работает, если не учитывать фон. Я более чем счастлив получить совет о более подходящих методах. Действия и методика определения падения - на основе этой статьи.

Мой код как есть, может быть видел здесь. Приносим извинения за количество отладочных отпечатков.

К вашему сведению - временной интервал 0.1 составляет одну десятую секунды или 100 миллисекунд, а не «0,1 миллисекунды».

rmaddy 09.06.2018 20:40

@rmaddy спасибо - просто оплошность в моем описании. У вас есть какие-нибудь советы по реализации класса RepeatingTimer?

George 09.06.2018 22:52

"специально таймеры GCD" Таймер - это таймер. Вы не уйдете от того факта, как они работают, только потому, что вы называете свой таймер другим именем. NSTimer, вероятно, даже использует таймер GCD под капотом.

matt 09.06.2018 22:56

Также неясно (1) какую проблему вы пытаетесь решить в реализации таймера, которую вы предлагаете, и (2) какую проблему вы пытаетесь решить в целом (возможно, таймер - не лучший способ)

matt 09.06.2018 22:58

@matt спасибо за ваш ответ. Я обновил свой вопрос, если у вас есть возможность взглянуть - в настоящее время я изучаю возможности использования HealthKit.

George 10.06.2018 00:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
188
1

Ответы 1

Это неправильный подход. Core Motion обеспечивает эффективный интерфейс для процессора движения, при этом вашему приложению не нужно постоянно запускать таймеры. Таймеры не будут учитываться в фоновом режиме, поэтому такой подход ни в коем случае не сработает. Вы видите некоторый дрейф в течение короткого периода фоновой активности, но если вы дадите ему еще несколько минут, вы обычно обнаружите, что ваше приложение полностью перестает генерировать события таймера.

Если вам нужны события акселерометра, установите CMMotionManager.accelerometerUpdateInterval на желаемое (0.1) и вызовите startAccelerometerUpdates(to:withHandler) для обработки событий. Не забудьте включить «Фоновый режим: обновления местоположения» в возможностях вашего приложения.

Тем не менее, как правило, вам не следует пытаться управлять фитнес-приложением на уровне Core Motion. Apple предоставляет гораздо более мощную и высокоуровневую поддержку через HKWorkout HealthKit. По общему признанию, HealthKit имеет невероятно раздражающий API, но он напрямую связывает вас с системой, которую iOS уже использует для отслеживания тренировок, поэтому вы получаете множество функций бесплатно (в частности, помогая решить множество сложных проблем с фильтрацией).

Правильно, это то, что я имел в виду в своем комментарии о том, «какую проблему вы на самом деле пытаетесь решить» ... Кстати, «невероятно раздражающий API». Но расскажите нам, что вы действительно думаете по этому поводу :)

matt 09.06.2018 23:26

@matt, в StackOverflow есть правила «соответствующего языка», которые не позволяют мне выразить свои глубокие чувства по поводу HealthKit, но это определенно правильный инструмент для решения определенных проблем.

Rob Napier 09.06.2018 23:50

@RobNapier, спасибо, что нашли время помочь здесь, я обновил свой вопрос, если у вас есть время взглянуть. Я обязательно посмотрю в HealthKit.

George 10.06.2018 00:30

То, что вы описали, определенно является работой Core Motion. Вы не можете реализовать это с помощью таймеров. Посмотрите на userAcceleration. Вероятно, это даст вам именно то, что вы хотите (не забывайте, что гравитация - это уклон в нисходящем направлении). Тем не менее, для обнаружения падения RotationRate может быть еще более показательным для того, что вы ищете, и уже был предварительно обработан для удаления гироскопических смещений. Возможно, вам удастся избежать гораздо более длительных периодов обновления, поскольку Core Motion уже выполняет большую часть анализа за вас.

Rob Napier 10.06.2018 02:47

"и работает, если не учитывать фон". Точно. Вы не можете решить проблему фонового изображения так, как вы собираетесь, поэтому вы должны пойти другим путем (который также оказывается лучшим способом).

Rob Napier 10.06.2018 02:48

Привет, @RobNapier, спасибо за вашу помощь. Я изучил Core Motion и согласился, что он может обеспечить непрерывный поток информации акселерометра (например, этот developer.apple.com/documentation/coremotion/…). Однако я не уверен, как это поможет с точки зрения обработки данных, чтобы определить, произошло ли падение дальше моей функции gatherAcc. Будет ли он основан на очереди и считывать эти значения из самой функции, а не создавать другую?

George 10.06.2018 13:13

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

Rob Napier 10.06.2018 15:35

Спасибо @RobNapier, однако мой исходный код полагался на несколько таймеров для проверки этих значений, и я не уверен, как это поможет устранить необходимость в них, поскольку они вызывают мою исходную проблему (время дрейфа). Хотя я могу добавить userAcceleration с помощью этого метода, проверка того, было ли падение каждые 2/3 секунды, безусловно, требует использования традиционных таймеров? По сути, метод проверки значений зависит от времени, я рассмотрю использование идентификаторов элементов.

George 10.06.2018 16:19

Позвольте нам продолжить обсуждение в чате.

George 10.06.2018 21:54

Другие вопросы по теме