开发过程中,出现耗时操作造成界面卡顿是常见的问题之一,问题原因就是因为耗时操作阻塞了主线程,所以要解决这类问题最简单的就是引进子线程,将耗时操作移出主线程,耗时操作完成后回到主线程中更新UI。
之前在做一个通讯录的时候,由于需要自己进行排序,所以第一次时需要将几千条的数据都拉下来然后处理,这是非常耗时的,直接放在主线程中网络请求然后各种处理数据会让界面卡死3到5秒。后来使用GCD的并发队列异步处理解决了,个人感觉非常的简单实用,关键代码如下:
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//耗时操作,如网络请求
//请求完成之后,回到主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
//更新UI操作
});
});
建议在开发过程中,页面的网络请求和耗时操作都可以以这种方式处理,一般每个页面的网络请求不会太多,不会造成并发数太大的问题,这也是用户体验和性能优化的一种,网络再慢也不会给用户带来卡顿的体验。
还有比较复杂一点的页面,可能存在多个操作,而某个操作依赖前几个操作完成之后才能开始,这个时候我们可以使用GCD的线程组,使用代码:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
//操作一
for (int i = 0; i < 10; i ++) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
//操作二
for (int i = 0; i < 10; i ++) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
//操作三
for (int i = 0; i < 10; i ++) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
dispatch_group_notify(group, queue, ^{
//最后的操作
for (int i = 0; i < 10; i ++) {
NSLog(@"notify------%@",[NSThread currentThread]);
}
});
我们先来看一下打印结果:
2018-08-30 15:04:19.178991+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.178995+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.179000+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.179238+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.179240+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.179243+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.179668+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.179910+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.179940+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.179983+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.180011+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.180341+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.180595+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.180753+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.180920+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.181272+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.181380+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.181513+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.181807+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.181906+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.182027+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.182437+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.182531+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.182616+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.182765+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.183371+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.183427+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.183489+0800 GCD_demo[3996:227745] 1------<NSThread: 0x604000261d00>{number = 3, name = (null)}
2018-08-30 15:04:19.183509+0800 GCD_demo[3996:227743] 3------<NSThread: 0x60c00007d080>{number = 5, name = (null)}
2018-08-30 15:04:19.183644+0800 GCD_demo[3996:227746] 2------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.184220+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.184694+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.185670+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.186316+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.186535+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.186660+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.187406+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.187940+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.188188+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
2018-08-30 15:04:19.188589+0800 GCD_demo[3996:227746] notify------<NSThread: 0x60800027f0c0>{number = 4, name = (null)}
我们会发现,操作一、操作二和操作三是无序执行的,但是最后的操作是等前面的都执行完成之后才开始执行的。像这样的应用场景还是很多的,比如第三个网络请求的参数依赖于前两个网络请求的结果,就可以用这种方式来处理。
如果想要操作一、二、三也都顺序执行,可以使用串行队列。还可以使用enter和leave方法,用法如下:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//操作一
NSLog(@"111");
dispatch_group_leave(group);
});
dispatch_group_async(group, queue, ^{
//操作二
NSLog(@"222");
});
dispatch_group_async(group, queue, ^{
//操作三
NSLog(@"333");
});
dispatch_group_notify(group, queue, ^{
//最后的操作
for (int i = 0; i < 10; i ++) {
NSLog(@"notify------%@",[NSThread currentThread]);
}
});
打印结果:
2018-08-30 15:34:01.152524+0800 GCD_demo[4391:254500] 111
2018-08-30 15:34:01.152530+0800 GCD_demo[4391:254497] 333
2018-08-30 15:34:01.152524+0800 GCD_demo[4391:254498] 222
2018-08-30 15:34:01.152854+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.153542+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.153710+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.153855+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.154183+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.154307+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.154711+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.154830+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.154944+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
2018-08-30 15:34:01.155039+0800 GCD_demo[4391:254498] notify------<NSThread: 0x60c00027c900>{number = 3, name = (null)}
以上代码可以保证操作一执行完才开始操作二和操作三,需要注意的一点是enter和leave方法是成对出现的,如果用了enter,没有写leave的话,会造成notify无法执行。如果操作一、二、三种存在block代码,那么leave方法需要放在block中,否则block没执行完,就开始执行后面的操作了。
虽然说多线程在开发和面试中都被认为是重中之重,但是就实际开发过程中而言,用到的场景并没有提供的方法多,没必要死扣理论和知道的方法多少,会用来解决问题才是最重要的!