#objective-c #ios #ios5 #nstimer #automatic-ref-counting
#objective-c #iOS #ios5 #nstimer #автоматический подсчет ссылок
Вопрос:
Я пытаюсь отладить свое приложение.
Я использовал некоторые экземпляры NSTimer в моем коде, отличном от arc, вот так (из основного потока):
[NSTimer scheduledTimerWithTimeInterval:5 target:musicPlayer selector:@selector(playPause:) userInfo:nil repeats:NO];
Это отлично работает, если я назначу этот код кнопке и нажму кнопку. Срабатывает таймер.
Я также пытался:
if( self.deliveryTimer == nil)
{
self.deliveryTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(playPause:) userInfo:nil repeats:NO];
}
-(void)playPause:(NSTimer*)timer
{
[deliveryTimer invalidate];
deliveryTimer = nil;
//more code here
}
Я бы ожидал, что таймер выполнится, нажмите метод воспроизведения / паузы ниже, затем переключитесь на nil, чтобы я мог сбросить таймер позже. Причина, по которой я проверяю значение nil, заключается в том, что у меня есть 3 разных пути кода, которые могут устанавливать таймер. У каждого есть инструкция NSLog, указывающая, что таймер был запланирован.
Мой код запускается, и я вижу, что таймеры запланированы, но, похоже, они не срабатывают в ходе обычного выполнения приложения. Я выясняю, почему. Краткосрочные таймеры, использующие ту же логику, срабатывают нормально. Проблемы возникают, когда я позволяю приложению работать некоторое время.
Могут ли NSTimers быть восстановлены ARC?
Имеет ли значение, устанавливаю ли я таймер из performSelectorInBackground? Когда я писал этот вопрос, я заметил, что некоторые из моих таймеров были созданы из пути кода, который вызывается через:
[self performSelectorInBackground:@selector(notifyDelegateOfDataPoint:) withObject:data];
может ли фоновый селектор быть причиной, по которой мои таймеры не запускаются / не восстанавливаются ранее?
Приветствуется любая помощь, эта ошибка не дает мне покоя более 2 недель!
Обновление: после изменения кода для использования основного потока для NSTimers таймеры срабатывают корректно, вызывая воспроизведение музыки:
[self performSelectorOnMainThread:@selector(deliverReminder:) withObject:nil waitUntilDone:NO];
-(void)deliverReminder:(id)sender{
[ NSTimer scheduledTimerWithTimeInterval:10 target:reminderDeliverySystem selector:@selector(playAfterDelay:) userInfo:nil repeats:NO];
[self postMessageWithTitle:nil message:@"Deliver Reminder Called" action:kNoContextAction];
}
-(void)playAfterDelay:(id)sender
{
int reminderDelay = reminder.delayValue.intValue;
[playTimers addObject:[NSTimer scheduledTimerWithTimeInterval:reminderDelay target:self selector:@selector(appMusicPlayerPlay:) userInfo:nil repeats:NO]];
}
Здесь у меня целая куча таймеров, это потому, что я не знаю, как передать примитив целевому объекту с помощью селектора.
Комментарии:
1. По-видимому, не срабатывают все таймеры или только некоторые? Что еще есть в их методе fire? Если таймер запланирован в фоновом потоке, он также сработает в этом потоке, что может привести к возникновению странных вещей.
2. Все таймеры, запланированные через performSelectorInBackground, действуют непоследовательно. Я слышал, как они воспроизводятся несколько раз, но в большинстве случаев они просто терпят неудачу. Я даже не получаю сообщение NSLog или обновление консоли. Вы правы насчет странных вещей, это точно описывает то, с чем я столкнулся.
Ответ №1:
NSTimer требует, чтобы цикл выполнения выполнялся в этом фоновом потоке, чтобы он продолжал запускаться. Основной поток уже имеет активный цикл выполнения, поэтому ваши таймеры отлично работают при выполнении в нем.
Если вы хотите использовать свои таймеры в фоновом потоке, вы можете сделать что-то вроде следующего:
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
self.deliveryTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(playPause:) userInfo:nil repeats:NO];
[runLoop run];
Что, вероятно, происходило с запуском ваших кратковременных таймеров, а более длительных — нет, так это то, что они запускались, когда поток все еще был активен, но без цикла выполнения, чтобы поддерживать его работу, завершались сбоем после того, как поток достиг конца своего выполнения.
Я не думаю, что это связано с ARC, хотя там может быть что-то, за чем вам придется следить, потому что NSRunLoop поддерживает таймер, который к нему подключен. Выполнение стандартной процедуры с NSTimers должно избежать проблем с ARC.
Комментарии:
1. Спасибо за разъяснение, теперь я понимаю, что происходило. Что касается фонового потока, существует ли только 1 или создается новый для каждого вызова? У меня performSelectorInBackground вызывается каждые несколько секунд, и, если я правильно понимаю, я бы создавал цикл выполнения только для нескольких экземпляров, когда таймер запланирован. Эти потоки будут продолжать выполняться с циклом выполнения до срабатывания таймера. Тогда поток завершится, и его ресурсы будут восстановлены. Правильно ли это?
2. @AlexStone — Каждый из этих вызовов должен создавать новый поток. Возможно, вам потребуется вручную прервать цикл выполнения, чтобы поток завершил его. Честно говоря, если все, что вы хотите сделать, это запустить задачу в фоновом режиме после определенной задержки, я бы посмотрел на GCD
dispatch_after()
, используя одну из глобальных параллельных очередей. Это будет проще, и это предотвратит накладные расходы от всех этих потоков.3. Стоит отметить две вещи о
[runLoop run]
: 1) управление не будет перемещаться дальше этой строки кода, пока цикл выполнения не будет завершен 2) вы не можете завершить цикл выполнения черезNSRunLoop
интерфейс — вам нужно получить базовыйCFRunLoop
и использоватьCFRunLoopStop()
. Просто еще больше причин использовать GCD!