Когда мне следует применять Runloop к моей программе и почему?

Мое требование состоит в том, что я хочу вызывать API, чтобы запрашивать новую информацию с моего сервера каждые 6 секунд, поэтому я написал свой код следующим образом:

MyBackgroundThread(){

    while(self.isStop){
        [self callMyAPI]; 
        [NSThread sleepfortimeinterval : 6 ];
    }
}

Но сегодня я узнаю, что в библиотеке Foundation есть способ написать цикл выполнения. Так что я могу переписать свой код, как показано ниже:

MyBackgroundThread(){

    NSTimer *timer = [NSTimer timerWithTimeInterval:6 target:self selector:@selector(callMyAPI) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [timer release];
    while (! self.isCancelled) {
        BOOL ret = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
}

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

Спасибо!

1 ответ

Решение

Я думаю, что обычно нет необходимости создавать новый цикл выполнения для таймера. Я бы предложил один из двух подходов:

  1. График NSTimer в главном цикле выполнения, но вызовите вызываемый метод и отправьте запрос в фоновую очередь.

  2. Создайте таймер отправки, запланированный для запуска в назначенной фоновой очереди отправки. Для этого создайте свойство таймера отправки:

    @property (nonatomic, strong) dispatch_source_t timer;
    

    а затем создать и запустить источник таймера отправки для запуска в указанной очереди GCD:

    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.polltimer", 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), kPollFrequencySeconds * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);
    
    dispatch_source_set_event_handler(self.timer, ^{
        <#code to be run upon timer event#>
    });
    
    dispatch_resume(self.timer);
    

Временами создание нового цикла выполнения полезно, но в этом простом сценарии это не нужно.


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

Таким образом, я был бы склонен к тому, что блок завершения callMyAPI сделать что-то простое, как:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6.0 * NSEC_PER_SEC)), queue, ^{
    <#code to issue next request#>
});

Это полностью устраняет необходимость в таймерах (и пользовательских циклах выполнения).


Наконец, если вам действительно нужно обнаруживать системные изменения с такой частотой, это может означать совершенно другую архитектуру сервера. Например, если вы проводите опрос каждые шесть секунд, чтобы узнать, изменилось ли что-то на сервере, вы можете рассмотреть реализацию на основе сокетов или использовать push-уведомления. При обоих этих подходах сервер будет сообщать клиентским приложениям о значительном событии, а не о приложении, которое ведет себя как Барт Симпсон на заднем сиденье автомобиля, постоянно спрашивая "мы уже здесь?"

Соответствующая архитектура, вероятно, зависит от того, с какой частотой изменяются данные сервера и каковы требования клиентского приложения.

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