实际操作中我们会发现,对于子线程来说,默认情况下它只会执行一次,执行完成后就会线程就为finished状态,只能销毁再创建。那我们想让该线程变为常驻线程,实时听从调度,该怎么做呢?很简单,用runloop即可。
iOS中runloop是和线程一一对应的,并且runloop是管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。默认情况下子线程的runloop是不会创建的,除非我们去调用它。
先来看个例子:
通过NSThread直接创建一个线程
1 | - (void)viewDidLoad { |
程序一开始打印test,然后我们点击这个btn,会发现没有任何反应。在点击事件处打断点,发现这个线程还是存在的,但是就是不起作用了。
解决方法:
1 | self.view.backgroundColor = [UIColor whiteColor]; |
我们都知道,Runloop启动前内部必须要有一个TImer/Observer/Source,所以在这里的runloop执行run之前,先创建了一个NSPort添加进去了。通常情况下,调用者需要持有这个port,并在外部线程通过这个port发送消息到loop内,但这里添加port只是为了让runloop不至于退出,并没有实际的发送消息。
这时候重新运行程序,点击btn,会在控制台上输出1234。
上面的换成GCD也一样,代码如下:
1 | - (void)viewDidLoad { |
点击btn,会在控制台上输出1234。
常驻线程的应用场景
日常开发过程中,耗时操作我们都会放在子线程中,比如下面这个代码:
1 | UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 450)]; |
运行后,我们滑动tableview,会发现很卡,无法顺畅滑动起来,这是因为这里的耗时操作我们放在了主线程中执行的。改进后代码如下:
1 | UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 450)]; |
将耗时操作放在子线程中,并且在子线程中开启runloop,并使子线程常驻,这样就能不停的执行耗时操作,并且不会影响到主线程啦,滑动tableview很丝滑。
参考文章:
RunLoop与线程的关系 (YYKit的作者写的)