ios开发之多线程
多线程的主要是用来执行一些耗时操作,例如网络图片、视频、歌曲、书籍等资源下载,游戏中的音乐播放等,充分发挥多核处理器的优势,并发(同时执行)任务让系统运行的更快、更流畅。
介绍下比较常用的多线程技术, 主要有NSObject、NSThread、NSOperation、GCD等。
1、 NSObject多线程技术
1> 使用performSelectorInBackground可以开启后台线程,执行selector选择器选择的方法
2> 使用performSelectorOnMainThread可以重新回到主线程执行任务,通常用于后台线程更新界面UI时使用
3> [NSThread sleepForTimeInterval:1.0f]; 让当前线程休眠,通常在程序开发中,用于模拟耗时操作,以便跟踪不同的并发执行情况!
但是,在程序发布时,千万不要保留此方法!不要把测试中的代码交给客户,否则会造成不好的用户体验。
提示:使用performSelectorInBackground也可以直接修改UI,但是强烈不建议使用。修改UI最好在主线程中执行
注意:在使用NSThread或者NSObject的线程方法时,一定要使用自动释放池,否则容易出现内存泄露。
1 - (void)viewDidLoad 2 { 3 [super viewDidLoad]; 4 5 // 在后台线程执行方法 6 [self performSelectorInBackground:@selector(testObject) withObject:nil]; 7 8 // 打印当前线程num 9 NSLog(@"main - %@", [NSThread currentThread]); 10 } 11 12 - (void)testObject 13 { 14 // 自动释放池,避免出现内存泄露 15 @autoreleasepool { 16 17 // 让当前线程睡眠 2.0 秒 18 [NSThread sleepForTimeInterval:2.0f]; 19 20 // 打印当前线程 21 NSLog(@"Background - %@", [NSThread currentThread]); 22 } 23 }
2、NSThread多线程技术
1> 类方法直接开启后台线程,并执行选择器方法
detachNewThreadSelector
2> 成员方法,在实例化线程对象之后,需要使用start执行选择器方法
initWithTarget
对于NSThread的简单使用,可以用NSObject的performSelectorInBackground替代
同时,在NSThread调用的方法中,同样要使用autoreleasepool进行内存管理,否则容易出现内存泄露。
1 - (void)viewDidLoad 2 { 3 [super viewDidLoad]; 4 5 // 开启新线程执行方法 6 [NSThread detachNewThreadSelector:@selector(testObject) toTarget:self withObject:nil]; 7 8 // // 通过实例化一个线程来开启任务 9 // NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testObject) object:nil]; 10 // 11 // [thread start]; 12 13 // 打印当前线程num 14 NSLog(@"main - %@", [NSThread currentThread]); 15 } 16 17 - (void)testObject 18 { 19 // 自动释放池,避免出现内存泄露 20 @autoreleasepool { 21 22 // 让当前线程睡眠 2.0 秒 23 [NSThread sleepForTimeInterval:2.0f]; 24 25 // 打印当前线程 26 NSLog(@"Background - %@", [NSThread currentThread]); 27 } 28 }
3、NSOperation多线程技术
1> 使用步骤:
1) 实例化操作
a) NSInvocationOperation
b) NSBlockOperation
2) 将操作添加到队列NSOperationQueue即可启动多线程执行
2> 更新UI使用主线程队列
[NSOpeationQueue mainQueue] addOperation ^{};
3> 操作队列的setMaxConcurrentOperationCount
可以设置同时并发的线程数量!
提示:此功能仅有NSOperation有!
4> 使用addDependency可以设置任务的执行先后顺序,同时可以跨操作队列指定依赖关系
提示:在指定依赖关系时,注意不要循环依赖,否则不工作。
1 @interface FLViewController () 2 { 3 NSOperationQueue *_queue; 4 } 5 @end 6 7 @implementation FLViewController 8 9 - (void)viewDidLoad 10 { 11 [super viewDidLoad]; 12 13 // 1. 初始化操作队列 14 _queue = [[NSOperationQueue alloc] init]; 15 16 // 2. 创建线程操作 17 // NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testObject) object:nil]; 18 19 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ 20 21 // 让当前线程睡眠 2.0 秒 22 [NSThread sleepForTimeInterval:2.0f]; 23 24 // 打印当前线程 25 NSLog(@"Background - %@", [NSThread currentThread]); 26 }]; 27 // 3. 开启线程 28 [_queue addOperation:op]; 29 }
4、GCD多线程技术-Grand Central Dispatch
GCD是基于C语言的框架,该多线程技术是苹果官方推荐使用的。
1> 要使用GCD,所有的方法都是dispatch开头的
2> 名词解释
global 全局
queue 队列
async 异步
sync 同步
3> 要执行异步的任务,就在全局队列中执行即可
dispatch_async 异步执行控制不住先后顺序
4> 关于GCD的队列
全局队列 dispatch_get_global_queue
参数:优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT
始终是 0
串行队列 dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
是创建得到的,不能直接获取
主队列 dispatch_get_main_queue
5> 异步和同步与方法名无关,与运行所在的队列有关!
提示:要熟悉队列于同步、异步的运行节奏,一定需要自己编写代码测试!
规律:并发队列(global/concurrent),根据函数名判断是同步执行(dispatch_sync)还是异步执行(dispatch_async)
串行队列(serial),无论后面的函数名是同步还是异步,均按照同步执行,并且,如果队列中第一个函数为同步,则该队列中所有的函数均在当前线程(一般为主线程)执行
/*! dispatch_async(sync)只负责将任务添加至操作队列,并不关心任务什么时候执行 * sync: 同步添加,也就说第一个任务A添加完成之后,要等它执行完成,才会添加任务B,然后执行任务B,再添加任务C,执行任务C…… * async: 异步添加,任务A添加完成以后,马上添加任务B,然后添加任务C,无论任务是否执行 * concurrent: 添加至该队列的任务都是并发的,也就是说,添加进来立刻分配线程执行 * serial: 添加至该队列的任务都是顺序执行的,也就是说,在一个线程上按添加顺序执行任务 */
同步主要用来控制方法被调用的顺序
1 - (void)viewDidLoad 2 { 3 [super viewDidLoad]; 4 5 // 创建一个全局队列,优先级为默认 6 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 7 8 dispatch_async(queue, ^{ // 添加异步任务1-block 9 NSLog(@"-----1- %@", [NSThread currentThread]); 10 }); 11 12 dispatch_async(queue, ^{ // 添加异步任务2-block 13 NSLog(@"-----2- %@", [NSThread currentThread]); 14 }); 15 16 dispatch_async(queue, ^{ // 添加异步任务3-block 17 NSLog(@"-----3- %@", [NSThread currentThread]); 18 }); 19 }