NSOperation、NSOperationQueue
NSOperation、NSOperationQueue
NSOperation 和 NSOperationQueue 配合使用也能实现多线程。
NSOperation 继承于 NSObject,是一种抽象类,并不具备封装操作的能力,必须使用它的子类。
使用 NSOperation 子类的三种方式:
1.NSBlockOperation;
2.NSInvocationOperation;
3.自定义 NSOperation,实现内部相应方法。
NSOperation 和 NSOperationQueue实现多线程的步骤:
1)先将需要执行的操作封装到一个 NSOperation(相当于GCD中的任务) 对象中
2)然后将 NSOperation 对象添加到 NSOperationQueue 中
3)系统会自动将 NSOperationQueue 中的 NSOperation取出来
4)将取出的 NSOperation 封装的操作放到一条新线程中执行。
NSInvocationOperation:
#pragma mark - NSInvocationOperation使用1(没什么卵用) - (void)invocationOp1 { // 1.创建操作 NSInvocationOperation *invocationOp =\ [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"invocationOp"]; // 2.启动。方法 - (void)start; 表示操作直接在当前线程执行 [invocationOp start]; } #pragma mark - NSInvocationOperation使用2 - (void)invocationOp2 { // 1.创建操作 NSInvocationOperation *invocationOp =\ [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"invocationOp"]; // 2. 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 3.操作添加到队列,就会异步调度执行这个方法 [queue addOperation:invocationOp]; } #pragma mark - NSInvocationOperation使用3 - (void)invocationOp3 { // 1. 创建队列( NSOperationQueue 本质上是对GCD中的并发队列的封装)。 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.创建操作并添加到队列 for (int i = 0; i < 10; i ++) { // 操作就是 GCD 里异步执行的任务 NSInvocationOperation *invocationOp =\ [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"invocationOp"]; // 3.操作添加到队列,就会异步调度执行这个方法 [queue addOperation:invocationOp]; } } - (void)downLoadImage:(id)obj { NSLog(@"NSThread=%@ obj=%@", [NSThread currentThread], obj); }
总结,NSOperationQueue 就是对 GCD 中队列的封装(主要是并发队列 和 主队列的封装)。而 NsOperation 相当于 GCD 中的任务。
NSBlockOperation
#pragma mark - NSBlockOperation - (void)blockOp1 { // 1.创建队列(相当于 GCD 的并发队列) NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.创建多个操作 for (int i = 0; i < 10; i ++) { NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"thread=%@ i=%d", [NSThread currentThread], i); }]; // 把操作放到队列 [queue addOperation:blockOp]; } } #pragma mark - NSBlockOperation更简单方法 - (void)blockOp2 { // 1.创建队列(相当于 GCD 的并发队列) NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.创建多个操作 for (int i = 0; i < 10; i ++) { [queue addOperationWithBlock:^{ NSLog(@"thread=%@ i=%d", [NSThread currentThread], i); }]; } } #pragma mark - NSOperationQueue相当于 GCD中的并发队列。但是也可以获取主线程和当前队列 - (void)operationQueue { // 1.创建队列(相当于 GCD 的并发队列) NSOperationQueue *queue = [NSOperationQueue mainQueue]; // 2.创建多个操作 for (int i = 0; i < 10; i ++) { // 操作就是 GCD 里异步执行的任务,并不会马上就执行 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"thread=%@ i=%d", [NSThread currentThread], i); }]; // 把操作放到队列 [queue addOperation:blockOp]; } NSLog(@"完成·········"); } 打印结果: 2016-04-05 22:45:20.723 NSOperation[746:37832] 完成········· 2016-04-05 22:45:20.724 NSOperation[746:37832] thread=<NSThread: 0x7fa781703410>{number = 1, name = main} i=0 2016-04-05 22:45:20.724 NSOperation[746:37832] thread=<NSThread: 0x7fa781703410>{number = 1, name = main} i=1 2016-04-05 22:45:20.725 NSOperation[746:37832] thread=<NSThread: 0x7fa781703410>{number = 1, name = main} i=2 2016-04-05 22:45:20.725 NSOperation[746:37832] thread=<NSThread: 0x7fa781703410>{number = 1, name = main} i=3 2016-04-05 22:45:20.725 NSOperation[746:37832] thread=<NSThread: 0x7fa781703410>{number = 1, name = main} i=4 2016-04-05 22:45:20.725 NSOperation[746:37832] thread=<NSThread: 0x7fa781703410>{number = 1, name = main} i=5
NSOperation:
- (void)invocation_block_Op { // 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; for (int i = 0; i < 6; i ++) { // 不创建操作,使用 - (void)addOperationWithBlock:(void (^)(void))block; 直接添加操作到队列 [queue addOperationWithBlock:^{ NSLog(@"耗时操作thread=%@ i=%d", [NSThread currentThread], i); }]; } // 操作 NSInvocationOperation *inOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"组团使用"]; [queue addOperation:inOp]; // block操作 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"blockOperationWithBlock - thread=%@", [NSThread currentThread]); }]; // - (void)addExecutionBlock:(void (^)(void))block; 添加的操作,和上面是同级别的操作 [blockOp addExecutionBlock:^{ NSLog(@"addExecutionBlock - thread=%@", [NSThread currentThread]); }]; [queue addOperation:blockOp]; } 打印结果: 2016-04-06 08:03:37.854 NSOperation[935:102551] thread=<NSThread: 0x7fdf0c801b40>{number = 19, name = (null)} i=0 2016-04-06 08:03:37.854 NSOperation[935:102549] thread=<NSThread: 0x7fdf0a4311d0>{number = 17, name = (null)} i=2 2016-04-06 08:03:37.854 NSOperation[935:102542] thread=<NSThread: 0x7fdf0a7f9950>{number = 21, name = (null)} i=1 2016-04-06 08:03:37.854 NSOperation[935:102544] thread=<NSThread: 0x7fdf0a60c940>{number = 16, name = (null)} i=3 2016-04-06 08:03:37.854 NSOperation[935:102551] thread=<NSThread: 0x7fdf0c801b40>{number = 19, name = (null)} i=4 2016-04-06 08:03:37.854 NSOperation[935:102549] thread=<NSThread: 0x7fdf0a4311d0>{number = 17, name = (null)} i=5 2016-04-06 08:03:37.855 NSOperation[935:102546] NSThread=<NSThread: 0x7fdf0c802ba0>{number = 20, name = (null)} obj=组团使用 2016-04-06 08:03:37.855 NSOperation[935:102542] blockOperationWithBlock - thread=<NSThread: 0x7fdf0a7f9950>{number = 21, name = (null)} 2016-04-06 08:03:37.855 NSOperation[935:102551] addExecutionBlock - thread=<NSThread: 0x7fdf0c801b40>{number = 19, name = (null)}
线程间通信:
#pragma mark - NSOperation 线程间通信 - (void)communicationBetweenThreads { // 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 添加操作,异步执行 [queue addOperationWithBlock:^{ // 耗时操作 // code···· NSLog(@"耗时操作 : %@", [NSThread currentThread]); // 主线程刷新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // code···· NSLog(@"刷新UI : %@", [NSThread currentThread]); }]; }]; } #pragma mark - GCD 实现线程通讯 - (void)GCD_threads { // 全局并发队列异步执行 dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 耗时操作 // code···· NSLog(@"耗时操作 : %@", [NSThread currentThread]); // 主线程刷新UI dispatch_async(dispatch_get_main_queue(), ^{ // code···· NSLog(@"刷新UI : %@", [NSThread currentThread]); }); }); } 打印结果: 2016-04-06 09:36:55.421 NSOperation[1150:152737] 耗时操作 : <NSThread: 0x7fd72866a9c0>{number = 4, name = (null)} 2016-04-06 09:36:55.421 NSOperation[1150:152700] 耗时操作 : <NSThread: 0x7fd7287aaf50>{number = 3, name = (null)} 2016-04-06 09:36:55.422 NSOperation[1150:152493] 刷新UI : <NSThread: 0x7fd728704dc0>{number = 1, name = main} 2016-04-06 09:36:55.422 NSOperation[1150:152493] 刷新UI : <NSThread: 0x7fd728704dc0>{number = 1, name = main}
最大并发数:
同时执行的操作的数量。
#pragma mark - 最大并发数 - (void)max_concurrent { // 设置最大并发数,是同时执行的操作的数量 self.queue.maxConcurrentOperationCount = 2; for (int i = 0; i < 10; i ++) { [self.queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2]; NSLog(@"%@", [NSThread currentThread]); }]; } } 打印结果: 2016-04-06 12:54:09.065 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)} 2016-04-06 12:54:09.065 NSOperation[2673:349573] <NSThread: 0x7fd5d8c8d700>{number = 2, name = (null)} 2016-04-06 12:54:11.071 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)} 2016-04-06 12:54:11.071 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)} 2016-04-06 12:54:13.073 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)} 2016-04-06 12:54:13.073 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)} 2016-04-06 12:54:15.078 NSOperation[2673:349573] <NSThread: 0x7fd5d8c8d700>{number = 2, name = (null)} 2016-04-06 12:54:15.078 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)} 2016-04-06 12:54:17.081 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)} 2016-04-06 12:54:17.081 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)} 分析:最大并发数是同时执行的操作的数量。以上代码,我们实现的是每两秒执行一次(查看打印的时间),每次执行两个操作。之所以所有相同的线程是因为:任务执行完毕之后,线程会被回收到线程池,然后再拿出来使用的过程!
挂起/恢复:
#pragma mark - 取消队列里面所有操作(已经执行的操作不会取消) // 取消操作,并不影响队列的挂起状态 - (IBAction)cancelAllOpeation:(id)sender { // 取消队列里面所有操作(队列里的操作都被移除) [self.queue cancelAllOperations]; NSLog(@"取消队列里面所有操作"); /** 场景:用户点击下载视频A、B、C、D; 然后点击 挂起队列(暂停); 再然后,点击取消所有操作; 最后,再次点击下载视频 -----> 导致无法下载,原因是 : 所有操作已取消。(点击挂起/恢复按钮可以实现下载) 添加如下代码,解决这个BUG */ // 取消队列挂起状态(只要取消了队列里的操作,我们就取消队列的挂起状态,以便于后续的开始) self.queue.suspended = NO; } #pragma mark - 挂起/继续(挂起是对队列的挂起,挂起之后,队列不再添加操作到队列去执行;但是挂起不会影响已经执行的操作) - (IBAction)pause:(id)sender { // 判断当前队列中是否有操作(防止用户先点击了“暂停/继续按钮”,导致队列被挂起之后,用户无法执行后续的操作); if (self.queue.operationCount == 0) { NSLog(@"没有操作!!"); return; } self.queue.suspended = !self.queue.isSuspended; if (self.queue.isSuspended) { NSLog(@"暂停"); } else { NSLog(@"继续"); } } 结论: @property (getter=isSuspended) BOOL suspended; // 挂起/恢复队列 @property (readonly) NSUInteger operationCount; // 队列里面的操作数 - (void)cancelAllOperations; // 取消所有操作(移除掉了) 可以配合使用,来实现需求。
设置依赖:
/** * @brief 添加数组操作 * * @param ops 数组操作 * @param wait 是否等待.YES : 等待前面的操作执行完毕再执行该方法后面的操作。 NO : 不等待前面的操作执行完毕就执行后面的操作。 */ - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
#pragma mark - 设置依赖 Methods - (void)dependency { NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"下载A:%@", [NSThread currentThread]); }]; NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"下载B:%@", [NSThread currentThread]); }]; NSBlockOperation *blockOp3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"刷新UI:%@", [NSThread currentThread]); }]; // 指定任务之间的依赖关系,依赖关系可以跨队列(子线程下载,主线程刷新UI)(不要出现循环依赖) [blockOp2 addDependency:blockOp1]; [blockOp3 addDependency:blockOp2]; // 并发队列中添加操作 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[blockOp1, blockOp2] waitUntilFinished:YES]; // 主队列刷新UI [[NSOperationQueue mainQueue] addOperation:blockOp3]; NSLog(@"执行完毕····"); }
参考文档: