Как я могу периодически запускать блок кода в фоновом режиме с помощью GCD?

Как я могу периодически запускать блок кода в фоновом режиме с помощью GCD? Я пытаюсь написать игровой движок с несколькими подсистемами, такими как рендеринг, физика, игровая логика и так далее. Некоторые задачи должны управляться событиями, но некоторые (например, физическую систему) следует периодически вызывать в фоновом режиме с постоянным временем (например, через 1/100 сек). Я создал блок кода, но как я могу запускать этот блок периодически в фоновом режиме? Правильный ли инструмент GCD?

Ответ 2

Как отмечает @matt в комментариях, для этого вы можете использовать источники отправки таймера. Посмотрите его ответ на правильный подход.

Для потомков, здесь мой оригинальный ответ с некоторыми альтернативами:

  • В обратном вызове рендеринга (скажем, используя CADisplayLink), запустите задание GCD, которое вычисляет физику для этого ( или следующий?). Если вам нужно сделать несколько обновлений для каждого кадра рендеринга, просто запустите этот цикл работы пару раз.

  • У вас есть физический поток, который спит сам, пока ему не потребуется некоторое вычисление. Если у вас есть отдельный поток для этого, NSTimer может иметь достаточное разрешение, чтобы разбудить поток до 100 Гц. (В основном потоке это не сработало, потому что другие источники ввода в runloop не позволят ему быстро запускать его.) Если NSTimer не работает, просто вычислите, сколько времени осталось до следующего физического обновления, которое вам нужно, и спящий поток (через, например, [NSThread sleepForTimeInterval:])

  • Имейте блок, который использует dispatch_after.

Что-то вроде этого:

dispatch_time_t next = dispatch_time(DISPATCH_TIME_NOW, 0);
block = ^{
    next = dispatch_time(next, 10000000L); // increment by 10 ms
    // do physics here
    dispatch_after(next, queue, block);
}

Вам может потребоваться немного умнее об отбрасывании кадров (т.е. определить, будет ли ваш следующий раз в прошлом до вызова dispatch_after и т.д.)

Ответ 3

В Swift вы можете создавать таймер с помощью GCD:

func CreateTimerDispatchSource(interval: UInt64, leeway: UInt64, queue: dispatch_queue_t, block: dispatch_block_t) -> dispatch_source_t {
    let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
    dispatch_source_set_timer(timer, dispatch_walltime(nil, 0), interval, leeway)
    dispatch_source_set_event_handler(timer, block)
    return timer;
}

var timer = CreateTimerDispatchSource(5*NSEC_PER_SEC, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
  // do some serious stuff
}

Запустить или возобновить таймер:

dispatch_resume(timer)

Таймер приостановки:

dispatch_suspend(timer)