线程保活
当子线程中的任务执行完毕后,线程就被立刻销毁了。如果程序中,需要经常在子线程中执行任务,频繁的创建和销毁线程,会造成资源的浪费。这时候我们就可以使用 RunLoop
来让该线程长时间存活而不被销毁。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 1.测试线程的销毁
[self threadTest];
}
/**
线程不保活
*/
- (void)threadTest
{
NSThread *subThread = [[NSThread alloc] initWithTarget:self selector:@selector(subThreadOpetion) object:nil];
[subThread start];
}
/**
子线程任务
*/
- (void)subThreadOpetion
{
NSLog(@"启动RunLoop后--%@",[NSRunLoop currentRunLoop].currentMode);
NSLog(@"%@----子线程任务开始",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.0];
NSLog(@"%@----子线程任务结束",[NSThread currentThread]);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(subThreadOpetion) onThread:self.subThread withObject:nil waitUntilDone:NO];
}
当子线程中的任务执行完毕后,线程就被立刻销毁了。如果程序中,需要经常在子线程中执行任务,频繁的创建和销毁线程,会造成资源的浪费。这时候我们就可以使用 RunLoop
来让该线程长时间存活而不被销毁。
/**
线程保活
*/
- (void)threadTest
{
NSThread *subThread = [[NSThread alloc] initWithTarget:self selector:@selector(subThreadEntryPoint) object:nil];
[subThread setName:@"HLThread"];
[subThread start];
self.subThread = subThread;
}
/**
子线程启动后,启动runloop
*/
- (void)subThreadEntryPoint
{
@autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//如果注释了下面这一行,子线程中的任务并不能正常执行
[runLoop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
NSLog(@"启动RunLoop前--%@",runLoop.currentMode);
[runLoop run];
}
}
/**
子线程任务
*/
- (void)subThreadOpetion
{
NSLog(@"启动RunLoop后--%@",[NSRunLoop currentRunLoop].currentMode);
NSLog(@"%@----子线程任务开始",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.0];
NSLog(@"%@----子线程任务结束",[NSThread currentThread]);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(subThreadOpetion) onThread:self.subThread withObject:nil waitUntilDone:NO];
}
我们将上面的示例代码修改一下,修改后的代码过程为,创建一个子线程,当子线程启动后,启动 RunLoop
,,RunLoop
添加了一个 NSMachPort
,这个 port
开启相当于添加了一个 Source1
事件源,但是这个事件源并没有真正的监听什么东西,只是为了不让 RunLoop
退出,从而达到了线程保活的目的。
RunLoop
启动前内部必须要有至少一个 Timer/Observer/Source
,所以 AFNetworking
在【runLoop run】
之前先创建了一个新的 NSMachPort
添加进去了。通常情况下,调用者需要持有这个 NSMachPort (mach_port)
并在外部线程通过这个 port
发送消息到 loop
内;但此处添加 port
只是为了让 RunLoop
不至于退出,并没有用于实际的发送消息。
RunLoop与线程
输出下边代码的执行顺序:
-(void)runloopEvent {
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
[self performSelector:@selector(test) withObject:nil afterDelay:10];
NSLog(@"3");
});
NSLog(@"4");
}
-(void)test {
NSLog(@"5");
}
答案是 1423,test
方法并不会执行。 原因是如果是带 afterDelay
的延时函数,会在内部创建一个 也就是如果当前线程没有开启 RunLoop
,该方法会失效。 那么我们改成:
-(void)runloopEvent {
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
[[NSRunLoop currentRunLoop]run];
[self performSelector:@selector(test) withObject:nil afterDelay:10];
NSLog(@"3");
});
NSLog(@"4");
}
-(void)test {
NSLog(@"5");
}
然而 test
方法依然不执行。 原因是如果 RunLoop
的 mode
中一个 item
都没有,RunLoop
会退出。即在调用 RunLoop
的 于其 mode
中没有添加任何 item
去维持 RunLoop
的时间循环,RunLoop
随即还是会退出。 所以我们自己启动 RunLoop
,一定要在添加 item
后
-(void)runloopEvent {
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
[self performSelector:@selector(test) withObject:nil afterDelay:1];
[[NSRunLoop currentRunLoop]run];
NSLog(@"3");
});
NSLog(@"4");
}
-(void)test {
NSLog(@"5");
}
这样就会输出14253
Demo:线程保活
网友评论