NSTimer требует, чтобы я добавил его в цикл выполнения

Мне интересно, может ли кто-нибудь объяснить, почему отправка обратно в основную очередь и создание повторяющегося NSTimer Мне нужно добавить его в RUN LOOP, потому что он тоже срабатывает? Даже при использовании performselectorOnMainThread мне все равно нужно добавить его в RUN LOOP, чтобы он сработал.

Ниже пример моего вопроса:

#define queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define mainqueue dispatch_get_main_queue()

- (void)someMethodBeginCalled
{
    dispatch_async(queue, ^{
        int x = 0;
        dispatch_async(mainqueue, ^(void){
            if([_delegate respondsToSelector:@selector(complete:)])
                [_delegate complete:nil];
        });
    });
}

- (void)compelete:(id)object
{
    [self startTimer];

    //[self performSelectorOnMainThread:@selector(startTimer) withObject:nil waitUntilDone:NO];
}

- (void)startTimer
{
    NSTimer timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(callsomethingelse) userInfo:nil repeats:YES];

    //NSDefaultRunLoopMode
    [[NSRunLoop currentRunLoop] addTimer:_busTimer forMode:NSRunLoopCommonModes];
}

EDIT: я считаю, что очень плохо сформулировал этот вопрос. Я хотел бы знать, почему [[NSRunLoop currentRunLoop] addTimer:_busTimer forMode:NSRunLoopCommonModes]; необходим в startTimer, если я вызываю someMethodBeginCalled. Если я не включу эту строку, таймер не сработает.

Например, если я вызову startTimer из viewDidLoad, я могу удалить строку NSRunLoop, и таймер будет срабатывать каждые 60 секунд.


person chicken    schedule 29.03.2012    source источник


Ответы (6)


Вместо этого вы всегда можете использовать этот метод:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(getBusLocation) userInfo:nil repeats:YES];

Это сэкономит вам строку, так как автоматически добавит ее в цикл выполнения.

person borrrden    schedule 29.03.2012
comment
Это нормальный подход. Редко можно вручную добавить таймер в цикл выполнения. Однако, как правило, вы должны хранить таймер в иваре, чтобы при необходимости можно было сделать его недействительным. - person Rob Napier; 29.03.2012
comment
Да, иначе объект, который он вызывает, никогда не будет освобожден, если у вас есть повторяющийся таймер. - person borrrden; 29.03.2012
comment
Я отредактировал вопрос, в основном я спрашиваю, почему я не могу использовать только вашу строку, когда я инициализирую ее из dispatch_async в основной очереди. Без строки NSRunLoop таймер не сработает. - person chicken; 29.03.2012
comment
Ах, теперь я понял. Я не знаю, почему это так... У меня относительно небольшой опыт работы с GCD ^^; - person borrrden; 29.03.2012
comment
Строка @borrrden должна работать, если вы вызываете ее в основной очереди с помощью dispatch_async(). Можете ли вы опубликовать код, который, по вашему мнению, не работает? Вы уверены, что используете scheduledTimer... (как это делает borrrden), а не просто timer... (как вы делали изначально)? - person Rob Napier; 29.03.2012
comment
Вау! я даже не заметил, что не использовал запланированныйTimerWithTimeInterval главный идиотский момент. Спасибо, Боррден/Роб. Ответ принят. - person chicken; 29.03.2012
comment
На самом деле должно быть NSTimer *timer... звездочку забыл - person erdekhayser; 28.09.2013
comment
@RobNapier вызывает запланированный таймер в основной очереди с помощью dispatch_async(). Так что цикл выполнения не нужен. Отлично работает. - person Dharma; 10.05.2018

А вот как добавить NSTimer в цикл выполнения:

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
person Rudolf Adamkovič    schedule 20.03.2013
comment
ОП спрашивает причину добавления таймера в цикл выполнения вместо того, как добавить таймер в цикл выполнения. - person chengsam; 17.05.2017

Потому что, как указано в документах< /а> сказать:

Таймеры работают в сочетании с циклами выполнения. Чтобы эффективно использовать таймер, вы должны знать, как работают циклы выполнения — см. Руководство по программированию NSRunLoop и Threading. В частности, обратите внимание, что циклы выполнения сохраняют свои таймеры, поэтому вы можете сбросить таймер после того, как добавили его в цикл выполнения.

Это проектное решение, которое Apple приняла, когда писала код для NSTimer (и я уверен, что у них были веские причины для этого), и мы ничего не можем сделать, чтобы обойти это. Неужели это так обременительно?

person sosborn    schedule 29.03.2012
comment
Для ясности: NSTimers реализованы как часть цикла выполнения. Один из источников цикла выполнения - есть ли какие-либо ожидающие NSTimers, которые необходимо запустить. NSTimer — это просто некоторые данные, которые зависают в цикле выполнения. Это очень хорошо, потому что это означает, что таймеры не требуют потоков. - person Rob Napier; 29.03.2012

Как сказал @sosborn, NSTimers зависят от NSRunLoops, и поскольку очереди GCD создают потоки, у которых нет циклов выполнения, NSTimer плохо сочетается с GCD.

Ознакомьтесь с другим вопросом StackOverflow по этому поводу: Безопасно ли планировать и аннулировать NSTimers в последовательной очереди GCD?

Чтобы решить эту проблему, я реализовал MSWeakTimer: https://github.com/mindsnacks/MSWeakTimer (и реализация была проверена инженером libdispatch на последнем WWDC!)

person Javier Soto    schedule 07.09.2013
comment
Я думаю, вы имели в виду, что NSTimer плохо работает с очередями GCD, верно? - person 0xced; 13.01.2014
comment
Правильный! Я исправил это. - person Javier Soto; 13.01.2014

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

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
   [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
       NSLog(@"Timer method from GCD main queue");
   }];

});

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

dispatch_async(dispatch_get_main_queue(), ^{
   [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
       NSLog(@"Timer method from GCD main queue");
   }];

});
person Say2Manuj    schedule 05.04.2017

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

    dispatch_async(dispatch_get_main_queue(), ^{
        self.timer = [NSTimer scheduledTimerWithTimeInterval:self.interval  invocation: self.invocation repeats:YES];
    });
person Chris Prince    schedule 14.02.2016