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入门小结