CFRunLoopRun () против [NSRunLoop run] - PullRequest
15 голосов
/ 21 декабря 2011

У меня есть объект NSRunLoop, к которому я присоединяю таймеры и потоки. Работает отлично. Остановка это совсем другая история.

Я запускаю цикл, используя [runLoop run].

Если я попытаюсь остановить цикл, используя CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]), цикл не остановится. Если вместо этого я запускаю цикл, используя CRunLoopRun(), он работает. Я также удостоверился, что вызов сделан в правильном потоке (тот, который выполняет мой пользовательский цикл выполнения). Я отладил это с pthread_self().

Я нашел архив списка рассылки, в котором разработчик сказал: «Не утруждайте себя использованием CRunLoopStop(), если вы запустили цикл с помощью метода run NSRunLoop». Я могу понять, почему это так - вы обычно объединяете инициализаторы и финализаторы из одного и того же набора функций.

Как вы можете остановить NSRunLoop без "обращения к CF"? Я не вижу stop метод на NSRunLoop. В документе сказано, что вы можете остановить цикл выполнения тремя способами:

  1. Конфигурирование цикла выполнения для запуска со значением времени ожидания
  2. Скажите циклу выполнения прекратить использование CFRunLoopStop()
  3. Удалите все входные источники, но это ненадежный способ остановить цикл выполнения, потому что вы никогда не можете знать, что застряло в цикле выполнения за вашей спиной

Ну, я уже попробовал 2. и это вызывает "уродливое" чувство, потому что вы должны копаться в CF. 3. не может быть и речи - мне не нравится недетерминированный код.

Это оставляет нам 1. Если я правильно понимаю документы, вы не можете «добавить» тайм-аут в уже существующий цикл выполнения. Вы можете запускать только новые циклы выполнения с таймаутом. Если я запускаю новый цикл выполнения, это не решит мою проблему, так как он создаст только вложенный цикл выполнения. Я все равно вернусь к старому, то же самое, что я хотел остановить ... верно? Возможно, я неправильно понял это. Кроме того, я не хочу запускать цикл со значением тайм-аута. Если я это сделаю, мне придется сделать компромисс между записью циклов ЦП (низкое значение тайм-аута) и отзывчивостью (высокое значение тайм-аута).

Это настройка, которая у меня есть сейчас (псевдокод):

Communicator.h

@interface Communicator : NSObject {
    NSThread* commThread;
}

-(void) start;
-(void) stop;
@end

Communicator.m

@interface Communicator (private)
-(void) threadLoop:(id) argument;
-(void) stopThread;
@end

@implementation Communicator
-(void) start {
    thread = [[NSThread alloc] initWithTarget:self 
                                     selector:@selector(threadLoop:)
                                       object:nil];
    [thread start];
}

-(void) stop {
    [self performSelector:@selector(stopThread)
                 onThread:thread
               withObject:self
            waitUntilDone:NO];
    // Code ommitted for waiting for the thread to exit...
    [thread release];
    thread = nil;
}
@end

@implementation Communicator (private)
-(void) stopThread {
    CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]);
}

-(void) threadLoop:(id) argument {
    // Code ommitted for setting up auto release pool

    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

    // Code omitted for adding input sources to the run loop

    CFRunLoopRun();
    // [runLoop run]; <- not stoppable with 

    // Code omitted for draining auto release pools

    // Code omitted for signalling that the thread has exited
}
@endif

Что мне делать? Это обычная / хорошая модель возиться с CF? Я недостаточно хорошо знаю Фонд. Возможно ли вмешательство на уровне CF (в отношении повреждения памяти, несоответствий, утечек памяти)? Есть ли лучший шаблон для достижения того, чего я пытаюсь достичь?

1 Ответ

9 голосов
/ 21 декабря 2011

У тебя все хорошо.Нет проблем в использовании CoreFoundation, когда вы не можете достичь своей цели с помощью Foundation.Поскольку CoreFoundation - это C, проще обойтись с управлением памятью, но при использовании CFRunLoop вместо NSRunLoop нет никакой внутренней опасности ( иногда это может быть даже безопаснее : CFRunLoop API являются потоковымибезопасно, тогда как NSRunLoop - нет).

Если вы хотите остановить NSRunLoop, вы можете запустить его, используя runMode:beforeDate:.runMode:beforeDate: возвращается, как только обработан входной источник, поэтому вам не нужно ждать, пока не истечет дата ожидания.

 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
 NSDate *date = [NSDate distantFuture];
 while ( !runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date] );

Затем, чтобы остановить цикл выполнения, вам просто нужно установить runLoopIsStopped до YES.

...