在iOS 里面,项目打开就会运行一个主线程,所有的UI都在主线程里进行.其他网络请求或者耗时操作理论上也可以在主线程运行,但是如果太耗时,那么就会影响主线程其他UI.所以需要开字线程来进行耗时操作,子线程进行完耗时操作之后,如果项目需求有需要刷新UI,或者改变UI,一定得回到主线程进行修改/刷新.
下面介绍三种iOS里线程模式
前情提要:在View上创建一个UILabel, 点击屏幕空白处开启一个子线程,在子线程里模拟耗时操作,耗时操作完毕后需要改变UILabel上的文字.
- (void)viewDidLoad
{
[super viewDidLoad];
self.markLabel = [[UILabel alloc]initWithFrame:CGRectMake(50, 200, 300, 40)];
self.markLabel.backgroundColor = [UIColor greenColor];
self.markLabel.textAlignment = NSTextAlignmentCenter;
self.markLabel.text = @"子线程开启之前的String";
[self.view addSubview:self.markLabel];
}
1.NSThread
1.1. 点击空白页面,开启线程(先模拟不在主线程刷新UI的错误写法)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//模拟线程耗时操作,并在耗时操作后改变label的String
[self threadModel];
}
- (void)threadModel
{
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadUpdateUI) object:nil];
[thread start];
}
- (void)threadUpdateUI
{
//模拟耗时操作
[NSThread sleepForTimeInterval:2];
//耗时操作后直接刷新UI (这是模拟错误的方法)
self.markLabel.text = @"修改后的Sting";
}
按照以上的写法,直接报错,报错提示如下图所示:
must be used from main thread only : 一定且只有从主线程刷新
因此可知,需要进入主线程去刷新,那么NSTread模式怎么进入主线程呢? 看下面的代码
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//模拟线程耗时操作,并在耗时操作后改变label的String
[self threadModel];
}
- (void)threadModel
{
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadUpdateUI) object:nil];
[thread start];
}
- (void)threadUpdateUI
{
//模拟耗时操作
[NSThread sleepForTimeInterval:2];
//NSThread 找到主线程
[self performSelectorOnMainThread:@selector(uodateMainUI) withObject:nil waitUntilDone:NO];
}
- (void)uodateMainUI
{
self.markLabel.text = @"修改后的Sting";
}
以上是NSThread 找到主线程,并且刷新UI的方法.
接下来介绍CGD和NSOperation 找到主线程的方法
2.CGD
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//模拟线程耗时操作,并在耗时操作后改变label的String
[self CGDModel];
}
- (void)CGDModel
{
//队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/*
我这里使用global_queue来模拟,你也可以使用并发队列/串行队列模拟
dispatch_queue_t queue = dispatch_queue_create(@"并发队列", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_queue_create(@"串行队列", DISPATCH_QUEUE_SERIAL);
但是不能用主队列来模拟,因为主队列本身就有主线程
*/
//任务
dispatch_async(queue, ^{
//模拟耗时操作
[NSThread sleepForTimeInterval:2];
//CGD模式回到主线程,因为CGD和NSOperation只有任务和队列的概念,所以主队列就是主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.markLabel.text = @"修改后的Sting";
});
});
}
3.NSOperation
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//模拟线程耗时操作,并在耗时操作后改变label的String
[self operationModel];
}
- (void)operationModel
{
//队列
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
//任务
[queue addOperationWithBlock:^{
//模拟耗时操作
[NSThread sleepForTimeInterval:2];
//找到主队列(NSOperation只有任务和队列的概念,所以主队列就是主线程)
[NSOperationQueue.mainQueue addOperationWithBlock:^{
self.markLabel.text = @"修改后的Sting";
}];
}];
}