iOS中的RunLoop

iOS 系统中,提供了两种RunLoop:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。

RunLoop使用场景
保持线程的存活,而不是线性的执行完任务就退出
1). 当线程不开启RunLoop
例子:

@interface TestThread : NSThread  
@end  

@implementation TestThread  
- (void)dealloc {  
    NSLog(@"%@线程被释放了", self.name);  
}  
@end  

@implementation ViewController  
- (void)viewDidLoad {  
    [super viewDidLoad];

    NSLog(@"%@----开辟子线程", [NSThread currentThread]);

    TestThread *testThread = [[TestThread alloc] initWithTarget:self selector:@selector(testThreadTodo) object:nil];
    testThread.name = @"testThread";
    [testThread start]; 
}

- (void)testThreadTodo {
    NSLog(@"%@----执行子线程任务", [NSThread currentThread]);
}
@end  

控制台输出:

<NSThread: 0x6000014c7680>{number = 1, name = main}----开辟子线程  
<TestThread: 0x6000014aaf80>{number = 3, name = testThread}----开始执行子线程任务  
testThread线程被释放了

即:子线程执行完操作就自动退出。

2). 当线程开启RunLoop
若子线程的操作是偶尔或者只需要执行一次的话,像上面那样就没什么问题。但是如果这个操作需要频繁执行,那么按照上面那样的逻辑,我们就需要频繁创建子线程,这样很消耗资源。
我们试试把线程“保持”起来,使控制器持有它,让它在需要的时候执行任务,不需要的时候就啥都不干。
此时,子线程在执行任务结束后不会被释放,但是若再次调用start方法,则程序会崩溃。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[TestThread start]: attempt to start the thread again'

执行完任务后,虽然线程没有被释放,还处于内存中,但是它处于死亡状态(当线程的任务结束后就会进入这种状态)。苹果不允许在线程死亡后再次开启。
为了让线程不进入死亡状态,只有让任务一直在进行中,可用一个死循环完成这种效果,但是子线程会不分时间地点场合的疯狂执行任务,并不可取。

此时不持有线程,使用runloop:

- (void)testThreadTodo {
    NSLog(@"%@----开始执行子线程任务", [NSThread currentThread]);
    //获取当前子线程的RunLoop
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    //让RunLoop跑起来
    [runLoop run];
    NSLog(@"%@----执行子线程任务结束", [NSThread currentThread]);
}

运行,控制台输出:

<NSThread: 0x600001f79dc0>{number = 1, name = main}----开辟子线程  
<TestThread: 0x600001f12400>{number = 3, name = testThread}----开始执行子线程任务

最后的打印却没有输出,说明任务并没有结束,线程也没有销毁。
RunLoop本质就是个Event Loop的do while循环,所以运行到[runLoop run]以后子线程就一直在进行接受消息->等待->处理的循环。所以不会运行[runLoop run]之后的代码,也就不会因为任务结束导致线程死亡进而销毁。这也就是我们最常使用RunLoop的场景之一,即保持线程的存活,而不是线性的执行完任务就退出。

3). 小结
何为RunLoop
顾名思义就是跑圈,他的本质就是一个do,while循环,当有事做时就做事,没事做时就休眠。

RunLoop和线程的关系

RunLoop是保证线程不会退出,并且能在不处理消息的时候让线程休眠,节约资源,在接收到消息的时候唤醒线程做出对应处理的消息循环机制。它是寄生于线程的,线程和RunLoop之间一一对应,主线程的RunLoop会在App运行的时自动运行,子线程需要手动获取运行,第一次获取时,才会去创建。

4). 参考
更深层次就不再赘述了,可参考大神博客:
深入理解RunLoop
iOS RunLoop入门小结