1

I am learning iOS runloop. Some articles on the net show me code like this:

- (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];
}

When using CFRunLoopRun(), it just goes fine. In each for-loop, a thread is created and then exited. However as for [runLoop run], the memory keeps growing and finally the app terminates due to "-[NSThread start]: Thread creation failed with error 35"(reaches the upper limit of thread count?)

**My question:

  1. What is the difference between -run() and CFRunLoopRun()? I thought the former is just a wrapper of the latter.

  2. The code seems to intend to show the right way to exit a thread. Is it practical in real-life developing?**

刘maxwell
  • 187
  • 10

1 Answers1

1

The CFRunLoopRun documentation tells us:

The current thread’s run loop runs in the default mode (see Default Run Loop Mode) until the run loop is stopped with CFRunLoopStop or all the sources and timers are removed from the default run loop mode.

But the run documentation doesn’t make any reference to that. It says:

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

But it goes on to warn:

Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. macOS can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.

If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:

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

where shouldKeepRunning is set to NO somewhere else in the program.

I’d also suggest you refer to Threading Programming Guide: Terminating a Thread.

You ask:

The code seems to intend to show the right way to exit a thread. Is it practical in real-life developing?

No, it’s exceedingly rare to write our own NSThread code anymore. Grand Central Dispatch (GCD) has eliminated all of these hassles. It’s more efficient (because it has pool of worker threads ready to go, doesn’t require a spinning loop or a NSRunLoop for each thread, etc.) and is much, much easier to write code. I wouldn’t suggest writing NSThread code unless there’s some very specific problem that you can’t easily solve with GCD.


By the way, note that when you write NSThread code, you should really have your thread set up its own autorelease pool (though we’d use @autoreleasepool { ... } instead of the pattern outlined in that guide). For example:

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

If you use GCD, this memory management is taken care of for you.


If you want more information about NSThread, NSRunLoop, etc., see the Threading Programming Guide. Or save yourself a lot of pain and just use GCD.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thank you so much for taking time to post such a detailed answer! Maybe I should have read the Apple documentations before asking this question. – 刘maxwell Apr 24 '19 at 03:03