executeSelectorInBackgroundThread и NSTimer - PullRequest
2 голосов
/ 29 июля 2009

Для игры, которую я разрабатываю, я вызываю дорогой метод из одной из процедур сенсорной обработки. Чтобы сделать это быстрее, я решил использовать performSelectorInBackgroundThread, поэтому вместо:

[gameModel processPendingNotifications];

Я перешел на:

[gameModel  performSelectorInBackground:@selector(processPendingNotifications) withObject:nil];

Первая проблема, с которой я столкнулся, это то, что у processPendingNotifications не было NSRunLoop, поэтому я добавил его следующим образом:

- (void)processPendingNotifications {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [pendingNotificationsQueue makeObjectsPerformSelector:@selector(main)];
    [pendingNotificationsQueue removeAllObjects];
    [pool drain];
}

Пока все хорошо. Моя проблема заключается в том, что некоторые методы, вызываемые из этого фонового потока, создают новые NSTimer экземпляры. Эти случаи заканчиваются тем, что не стреляют. Я думаю, это потому, что у вторичного потока, который у меня есть, нет (или он заканчивается) NSRunLoop. Я запускаю новые таймеры с помощью:

[NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(timerFired) userInfo:nil repeats:NO];

Мои вопросы:

  1. Правильно ли я подозреваю, что проблема связана с NSRunLoop?
  2. Можно ли запустить NSTimer из фонового потока и прикрепить его к основному потоку NSRunLoop?

Ответы [ 2 ]

7 голосов
/ 29 июля 2009

Да, вам нужен runloop для отправки событий таймера

NSTimers неявно присоединены к циклу выполнения потока, в котором они созданы, поэтому, если вы хотите, чтобы он был присоединен к циклу выполнения основного потока, создайте его в основном потоке, используя executeSelectorOnMainThread: withObject: waitUntilDone :. Конечно, этот код будет выполняться в главном потоке.

Если ваш вопрос «Можно ли запустить таймер в цикле выполнения основного потока, который напрямую запускает селектор в другом потоке?» Ответ - нет, но селектор, который он запускает в основном потоке, может просто выполнитьSelector: onThread: withObject: waitUntilDone :. Конечно, для этого требуется, чтобы поток, с которым вы пытаетесь выполнить селектор, имел рабочий цикл запуска.

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

3 голосов
/ 29 июля 2009

NSTimers фактически просто периодически запускают события во вмещающий NSRunLoop, который есть у каждого потока (или должен иметь). Итак, если у вас есть дочерний (или фоновый) процесс, запущенный в другом потоке, NSTimers будет запускаться против NSRunLoop этого потока вместо основного NSRunLoop приложения.

Вы можете убедиться, что таймеры всегда создаются для основного цикла выполнения, отправив ему сообщение addTimer: forMode: с новым экземпляром (но не запущенным) NSTimer. Доступ к циклу выполнения основного приложения осуществляется с помощью [NSRunLoop mainRunLoop], поэтому независимо от того, в каком потоке вы находитесь, выполните [[NSRunLoop mainRunLoop] addTimer: [NSTimer timerWithTimeInterval: 20.0 target: self selector: @selector (timerFired) userInfo: nil повторяется: НЕТ]] forMode: NSDefaultRunLoopMode] всегда будет планировать таймер для основного цикла выполнения.

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

Стоит рассмотреть NSOperation и NSOperationQueue, если вы действительно хотите, чтобы фоновая активность происходила.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...