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(@"执行完毕····");
}

参考文档:

NSOperation官方文档

NSOperationQueue官方文档

时间: 2024-07-29 11:26:29

NSOperation、NSOperationQueue的相关文章

NSOperation、NSOperationQueue(II)

NSOperationQueue 控制串行执行.并发执行 NSOperationQueue 创建的自定义队列同时具有串行.并发功能 这里有个关键属性 maxConcurrentOperationCount,叫做最大并发操作数.用来控制一个特定队列中可以有多少个操作同时参与并发执行. 最大并发操作数:maxConcurrentOperationCount maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行. maxConcurrentOpera

NSOperation、NSOperationQueue(III)

NSOperation.NSOperationQueue 常用属性和方法归纳 NSOperation 常用属性和方法 a. 取消操作方法 //可取消操作,实质是标记 isCancelled 状态. - (void)cancel; b. 判断操作状态方法 1 - (BOOL)isFinished; 判断操作是否已经结束. 2 3 - (BOOL)isCancelled; 判断操作是否已经标记为取消. 4 5 - (BOOL)isExecuting; 判断操作是否正在在运行. 6 7 - (BOOL

iOS自学之NSOperation、NSOperationQueue、Background

iOS中多线程编程主要分为NSThread.NSOperation和GCD,今天主要记录下自己在学习NSOperation中的点滴-如有不对的地方帮忙指出下,PS:人生第一次写blog,各位看官请轻虐,谢啦- NSOperation是abstract类,不能直接使用,可以使用CocoTouch提供的NSBlockOperaion和NSInvocationOperation,也可以自己实现subclass.NSOperation可以理解为一个独立的任务,没有调度功能,真正利用NSOperation

【iOS开发】多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用

http://blog.csdn.net/crycheng/article/details/21799611 本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种: 1> 自定义子类继承NSOperation,实现内部相应的方法 2> NSB

【iOS开发-91】GCD的同步异步串行并行、NSOperation和NSOperationQueue一级用dispatch_once实现单例(转载)

(1)GCD实现的同步异步.串行并行. ——同步sync应用场景:用户登录,利用阻塞 ——串行异步应用场景:下载等耗时间的任务 /** * 因为是异步,所以开通了子线程,但是因为是串行队列,所以只需要开通1个子线程(2),它们在子线程中顺序执行.最常用. */ -(void)gcdDemo1{ dispatch_queue_t q1=dispatch_queue_create("com.hellocation.gcdDemo", DISPATCH_QUEUE_SERIAL); for

多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用

本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种: 1> 自定义子类继承NSOperation,实现内部相应的方法 2> NSBlockOperation 3>NSInvocationOperation 这讲先介绍如何用NSOper

GCD的同步异步串行并行、NSOperation和NSOperationQueue一级用dispatch_once实现单例

转:http://www.tuicool.com/articles/NVVnMn (1)GCD实现的同步异步.串行并行. ——同步sync应用场景:用户登录,利用阻塞 ——串行异步应用场景:下载等耗时间的任务 /** * 因为是异步,所以开通了子线程,但是因为是串行队列,所以只需要开通1个子线程(2),它们在子线程中顺序执行.最常用. */ -(void)gcdDemo1{ dispatch_queue_t q1=dispatch_queue_create("com.hellocation.gc

IOS中的多线程【二】— NSOperation和NSOperationQueue

NSOperationQueue是一套基于Objective-c语言的API. GCD与NSOperationQueue的优缺点: NSOperationQueue:比较安全 GCD:没有NSOperationQueue安全,但使用起来简单,快速,还提供了一些操控底层的方法.实际开发中还是以GCD为主. NSOperationQueue实现多线程流程 1.定义一个任务队列. 2.定义一个任务. 3.把任务添加到队列中.一旦任务被添加到队列中,任务会马上被调度执行. 任务队列(NSOperatio

多线程之NSOperation和NSOperationQueue

这篇文章里我将不过多的谈及理论知识,这些东西会的自然会,不会的,看多了也是云里雾里.下面我讲更多的用代码+注释的方式来讲如何使用NSOperation和NSOperationQueue. 1.NSOperation.是抽象类,不能够直接使用,而是使用子类NSInvocationOperation和NSBlockOperation来实际执行任务.NSOperation本身和多线程是没有任何关系的,她只是封装了一个代码段和数据去实现一个功能. 1.1.NSInvocationOperation,基于