本文共 6545 字,大约阅读时间需要 21 分钟。
iOS开发之多线程
本文章博主和大家一块学习多线程,很自然就涉及到线程和进程,然后涵括NSThread、GCD、NSOperation!然后就是最牛叉的RunLoop和Runtime。
一、线程与进程
进程是指在系统中正在运行的一个应用程序,就是一段程序的执行过程,每个进程之间是相互独立的。一个进程要想执行任务,必须要有线程,至少有一条线程,一个进程的所有任务都是在线程中执行。其实应用程序启动的时候我么你的系统会默认帮我们的应用程序开启一条线程,这条线程也叫做“主线程”,或者“UI线程”。
二、多线程
多线程的优点和缺点
多线程的应用
1.使用单利模式时,可以使用GCD 2.耗时操作放入子线程处理,完成后回主线程显示。 3.从数据库读取大量数据,可开辟子线程操作。 4.处理音频、视频数据时,在子线程处理。 5.数据同步操作。三、容易混淆的4个术语
同步和异步主要影响:能不能开启新的线程。
1.同步:只是在当前线程中执行任务,不具备开启新线程的能力。 2.异步:可以在新的线程中执行,具备开启新线程的能力。并发和串行主要影响:任务的执行方式
并发:多个任务并发(同时)执行。 串行:一个任务执行完毕后,再执行下一个任务。四、NSThread、NSOperation、GCD
1.NSThread
1)通过NSThread的对象方法(动态创建)2)通过NSThread的类方法(静态创建)
NSThread的线程管理NSThread的线程通信
2.NSOperation
2.1 NSOperation 基本使用 在iOS开发中,为了提升用户体验,我们通常会将操作耗时的操作放在主线程之外的线程进行处理。对于正常的简单操作,我们更多的选择代码更少的GCD,让我们专注于自己的业务逻辑开发。NSOperation在iOS4后也基于GCD实现,但是对于GCD来说可控性更强,并且可以加入操作依赖。 (1) 相关概念 NSOperation是对GCD的封装,其中有两个核心概念【队列+操作】 1.NSOperation本身是抽象类,只能有它的子类。 2.两大子类分别是:NSBlockOperation、NSInvocationOperation,当然你也可以自定义继承自NSOperation的类。(2)基本使用
NSInvocationOperation类
// 01 NSInvocationOperation创建线程/* 第一个参数:目标对象 第二个参数:该操作要调用的方法,最多接受一个参数 第三个参数:调用方法传递的参数,如果方法不接受参数,那么该值传nil */- (void) method1 { NSString * imageUrl = @"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=482494819,3150422682&fm=200&gp=0.jpg"; NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imageUrl];// [invocationOperation start]; //一旦执行操作,就会调用target的sel方法, 默认情况下调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作,只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; [queue addOperation:invocationOperation];}//更新imageView- (void)updateImage:(NSData *)data{ //在主线程中更新UI //将二进制数据转换为图片 UIImage *image=[UIImage imageWithData:data]; //设置image self.imageView.image=image;}
NSBlockOperation类
队列的取消、暂停、和恢复
// 恢复队列,继续执行// self.queue.suspended = NO;// 暂停(挂起)队列,暂停执行// self.queue.suspended = YES;// 取消队列的所有操作[self.queue cancelAllOperations];
操作依赖
NSOperation之间可以设置依赖来保证执行顺序
不添加依赖之前op1、op2、op3的顺序是随机的在不同queue的NSOperation之间创建依赖关系
在不加依赖之前,op1和op2的执行顺序是随机的,添加依赖后op2会在op1之后执行。线程间的通讯
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self updateImage:data]; }];
3.GCD
3.1基本概念
全称是Grand Central Dispatch,即:强大的中枢调度器,它是纯C语言的,提供了非常多强大的函数。3.2GCD的优势:
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
3.3GCD的使用步骤:
指定任务:确定想要做的事
将任务添加到队列中:GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出。
3.4GCD的队列
3.4.1 GCD的队列可以分为2大类型:
1.并发队列(Current Dispatch Queue)
2.串行队列(Serial Dispatch Queue)
3.4.2 使用dispatch_queue_create函数创建队列
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);其中:const char *label 列队列的名称 dispatch_queue_attr_t attr 队列的类型
创建一个并发队列 dispatch_queue_t queue1 = dispatch_queue_create("myQueue1", DISPATCH_QUEUE_CONCURRENT);
创建一个串行队列 dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
GCD默认意境提供了全局的并发队列,供整个应用使用,可以无需手动创建
使用dispatch_get_global_queue 函数获得全局并发队列 //获得全局的并发队列 dispatch_queue_t queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 其中:第一个参数代表全局并发队列的优先级 #define DISPATCH_QUEUE_PRIORITY_HIGH 2 --》 高 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 --》 默认(中) #define DISPATCH_QUEUE_PRIORITY_LOW (-2) --》 低 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN --》 后台 第二个参数标记:是为了未来使用保留的!所以这个参数应该永远指定为0
GCD中获得串行有2种途径
1.使用dispatch_queue_create 创建串行队列,创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL) dispatch_queue_t queue4 = dispatch_queue_create("com.520.queue", NULL);2.使用主队列 (跟主线程相关联的队列),主队列时GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放在主线程中执行,dispatch_get_main_queue()获得主队列 dispatch_queue_t queue5 = dispatch_get_main_queue();
3.4.3 应用
3.4.3.1 串行队里
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
#pragma mark - 串行队列+同步任务注意代码执行的顺序,同步时并没有开辟新线程。
#pragma mark - 串行队列+异步任务
注意代码顺序,异步时开辟了新的线程,在新线程里,任务一个一个的执行,如果把第二个任务里面sleep5秒,任务仍然串行执行。如下图:
3.4.3.2.并发队列(Concurrent Dispatch Queue)
#pragma mark - 并发队列+同步任务
注意观察,没有开辟新线程,任务同步执行。#pragma mark - 并发队列+异步任务注意观察,开辟了多条子线程,任务异步执行。
3.4.3.3.线程间的通信
dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程,执行UI刷新操作 });
3.4.3.4.其他应用
1.延时执行
iOS常见三种延时执行方式 1)调用NSObject的方法[self performSelector:@selector(需要执行的方法名) withObject:nil afterDelay:2.0]
2)使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后执行这里的代码.. });
3)使用定时器
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(需要执行的方法名) userInfo:nil repeats:NO];
2.一次性代码
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //里面的代码只会被执行一次 });
3.队列组
有这么一个需求,首先:分别异步执行2个耗时的操作,其次:等2个异步操作都执行完毕后,再回到主线程执行操作,如果想要快速高效地实现上述需求,可以考虑用队列组。
dispatch_group_tgroup = dispatch_group_create(); dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行1个耗时的异步操作 }); dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行1个耗时的异步操作 }); dispatch_group_notify(group,dispatch_get_main_queue(), ^{ // 等前面的异步操作都执行完毕后,回到主线程... });
4.快速迭代
我只想说速度非常快,要加一个__block因为block代码默认不能改外面的东西5.barry执行任务函数
在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行。
下面的例子是在添加到队列的任务1、任务2、任务3执行完毕后,然后才执行barrier,barrier执行完毕后才执行任务4、任务5。dispatch_barrier在并发队列中创建一个同步点,当并发队列中遇到一个 dispatch_barrier时,会延时执行该 dispatch_barrier,等待在 dispatch_barrier之前提交的任务block执行完后才开始执行,之后,并发队列继续执行后续block任务。
6.GCD与定时器
@property (nonatomic,strong) dispatch_source_t timer;
- (void) myTimer { self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); // 设置定时器的触发时间(1秒后)和时间间隔(每隔2秒) dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), 2 * NSEC_PER_SEC, 0); // 设置回调 dispatch_source_set_event_handler(self.timer, ^{ NSLog(@"Timer %@", [NSThread currentThread]); }); // 开始定时器 dispatch_resume(self.timer);}
-(void)viewDidDisappear:(BOOL)animated { dispatch_cancel(self.timer); self.timer = nil;}
转载地址:http://lwcti.baihongyu.com/