В чем разница между вызовом (NSRunLoop) -run () и CFRunLoopRun () - PullRequest
1 голос
/ 23 апреля 2019

Я изучаю iOS runloop.Некоторые статьи в сети показывают мне такой код:

- (void)memoryIssue {
    for (int i = 0; i < 10000; i++) {
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread) object:nil];
        [thread setName:thread_name];
        [thread start];
        [self performSelector:@selector(stopThread) onThread:thread withObject:nil waitUntilDone:YES];
    }
}

- (void)runThread {
    NSLog(@"current thread = %@", [NSThread currentThread]);
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    static NSMachPort *port;
    if (!port) {
        port = [NSMachPort port];
    }
    [runLoop addPort:port forMode:NSDefaultRunLoopMode];
//    CFRunLoopRun();  //All right
    [runLoop run];  // ⚠️Thread not exit...!
}

- (void)stopThread {
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSThread *thread = [NSThread currentThread];
    [thread cancel];
}

При использовании CFRunLoopRun() все идет хорошо.В каждом цикле for создается поток, который затем закрывается.Однако, что касается [runLoop run], память продолжает расти, и, наконец, приложение завершает работу из-за "- [NSThread start]: создание потока завершилось ошибкой 35" (достигнут верхний предел числа потоков?)

**Мой вопрос:

  1. В чем разница между -run() и CFRunLoopRun()?Я думал, что первый - просто обертка второго.

  2. Код, похоже, намеревается показать правильный способ выхода из потока.Практично ли это в реальной жизни? **

1 Ответ

0 голосов
/ 23 апреля 2019

Документация CFRunLoopRun сообщает нам:

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

Но документация run не содержит ссылок на это. Это говорит:

Если входные источники или таймеры не подключены к циклу выполнения, этот метод завершается немедленно; в противном случае он запускает приемник в NSDefaultRunLoopMode, повторно вызывая runMode:beforeDate:. Другими словами, этот метод эффективно запускает бесконечный цикл, который обрабатывает данные из входных источников и таймеров цикла выполнения.

Но он продолжает предупреждать:

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

Если вы хотите завершить цикл выполнения, вы не должны использовать этот метод. Вместо этого используйте один из других методов выполнения, а также проверьте другие произвольные условия в цикле. Простой пример:

BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

где shouldKeepRunning установлен в NO где-то еще в программе.

Я также предлагаю вам обратиться к Руководство по программированию потоков: Завершение потока .

Вы спрашиваете:

Код, похоже, намеревается показать правильный способ выхода из потока. Практично ли это в реальной жизни?

Нет, мы редко пишем собственный код NSThread. Grand Central Dispatch (GCD) устранил все эти неприятности. Он более эффективен (потому что он имеет пул рабочих потоков, готовых к работе, не требует циклического вращения или NSRunLoop для каждого потока и т. Д.) И намного, намного проще для написания кода. Я бы не советовал писать код NSThread, если нет какой-то очень специфической проблемы, которую трудно решить с помощью GCD.


Кстати, обратите внимание, что когда вы пишете NSThread код, вам действительно нужно, чтобы ваш поток настроил свой собственный пул автоматического выпуска (хотя мы использовали бы @autoreleasepool { ... } вместо шаблона, описанного в это руководство). Например:

- (void)runThread {
    @autoreleasepool {
        ...
    }
}

Если вы используете GCD, это управление памятью позаботится о вас.


Если вам нужна дополнительная информация о NSThread, NSRunLoop и т. Д., См. Руководство по программированию потоков . Или избавьте себя от боли и просто используйте GCD .

...