CFRunLoopRun() vs [NSRunLoop run]

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

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

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

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

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

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

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

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

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

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

У тебя все хорошо. Там не проблема использовать 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.