美文网首页
iOS多线程之NSOperationQueue、NSOperat

iOS多线程之NSOperationQueue、NSOperat

作者: 尛焱 | 来源:发表于2018-11-09 15:58 被阅读27次

    NSOperationQueue操作队列根据其优先级和准备情况执行排队的NSOperation对象。 添加到操作队列后,操作将保留在其队列中,直到它报告已完成其任务。 添加后,您无法直接从队列中删除操作。
    从多个线程使用单个NSOperationQueue对象是安全的,没必要创建额外的 lock, NSOperationQueue类的属性都是默认atomic的,关于锁和线程安全的内容在后续的文章中详叙
    NSOperationQueue都是和NSOperation子类(NSInvocationOperationNSBlockOperation)一起使用的,下面先介绍NSOperationQueue

    NSOperationQueue常用属性和方法:

    1. mainQueue

    返回与主线程关联的操作队列。

    2. currentQueue

    返回当前操作的操作队列。

    3. name

    操作队列名称,可用于运行时标识操作队列,此属性的默认值是包含操作队列的内存地址的字符串。

    4. maxConcurrentOperationCount

    可同时进行的最大操作数,设置为1相当于串行队列,运行时减少并发操作的数量不会影响当前正在执行的任何操作。 指定值(也是默认值)NSOperationQueueDefaultMaxConcurrentOperationCount(-1)会导致系统根据系统条件设置最大操作数。(比如根据 CPU 最大核心数,这个数据可用[NSProcessInfo processInfo].activeProcessorCount获得)

    5. operations

    返回当前在队列的所有操作,此属性中的数组按其添加到队列的顺序返回。 此顺序不一定代表执行这些操作的顺序。

    6. operationCount

    返回当前在队列的操作的数量,返回的值只表示访问该属性时的临时状态,因为队列中的操作数会随着操作的完成而更改,因此,请勿将此值用于operations属性的枚举等

    6. qualityOfService

    应用于使用队列执行的操作的默认优先级。如果添加到队列的operation有自己的优先级,则使用operation的优先级;对于您自己创建的队列,默认值为NSOperationQualityOfServiceBackground。 对于mainQueue方法返回的队列,默认值为NSOperationQualityOfServiceUserInteractive,无法更改。

    7. suspended

    布尔值,指示队列是否是暂停状态。将此属性设置为YES可防止队列启动任何排队操作,但已执行的操作将继续执行。 您可以继续向已暂停的队列添加操作,但在将此属性更改为NO之前,不会执行这些操作。

    8. underlyingQueue

    用于执行队列的操作,默认为nil,仅当队列中没有操作时,才可以设置此属性的值; 当operationCount不等于0时设置此属性的值会引发NSInvalidArgumentException。 此属性的值不能设置成主线程。 此队列设置的优先级别将覆盖为操作队列的qualityOfService属性设置的任何值。

    9. addOperation

    添加一个操作到队列中,操作对象一次最多只能在一个操作队列中,如果操作已经在另一个队列中,则此方法抛出NSInvalidArgumentException异常。 同样,如果操作当前正在执行或已经完成执行,则此方法抛出NSInvalidArgumentException异常。

    10. addOperationWithBlock

    将一个无参数无返回值的block包装成对象,然后添加到队列中; 您不应尝试获取对新创建的操作对象的引用或确定其类型信息。

    11. addOperations:waitUntilFinished:
    - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
    

    将操作数组Operations添加到队列中,其中wait:如果 是YES,则阻止当前线程,直到所有操作完成执行。 如果为NO,立即返回。

    NSOperation

    因为NSOperation类是一个抽象类,所以不要直接使用它,而是使用系统定义的子类(NSInvocationOperationNSBlockOperation)来执行实际任务。跟NSOperationQueue一样,可以安全地从多个线程调用NSOperation对象的方法,而无需创建额外的锁来同步对象的访问。

    1. isReady

    isReady表示操作何时可以执行。 当操作准备好立即执行时,值为YES

    2. isExecuting

    如果操作正在处理其任务,则isExecuting属性返回YES,否则返回NO

    3. isFinished

    isFinished表示操作已成功完成任务或已取消并正在退出。 在isFinished的值更改为YES之前,操作对象不会清除依赖关系。 类似地,在isFinished属性包含值YES之前,操作队列不会使操作出列。

    4. isCancelled

    isCancelled表示操作是否调用了取消方法。
    将操作添加到队列后,如果你稍后决定不想执行操作 ( 比如用户在页面中按下取消按钮或退出应用程序,这时可以取消操作以防止它不必要地消耗CPU时间)。 您可以通过调用操作对象本身的cancel方法或通过调用NSOperationQueue类的cancelAllOperations方法来完成取消操作。
    取消操作不会立即停止正在进行的操作。 你的代码必须检查此属性中的值并根据需要中止操作。 NSOperation的默认实现包括检查取消, 例如,如果在调用start方法之前取消操作,则start方法退出而不启动任务。

    5. start

    此方法的默认实现更新操作的执行状态并调用main方法。 此方法还执行多项检查以确保操作可以实际运行。 例如,如果操作被取消或已经完成,则此方法只返回而不调用main。如果操作当前正在执行或未准备好执行(isReady),则此方法将引发NSInvalidArgumentException异常。

    6. completionBlock

    操作任务完成后执行的块。此Block块不带参数,也没有返回值。不应该使用此Block块来执行任何需要特定上下文的工作。 相反,你应该将该工作分流到应用程序的主线程或能够执行此操作的特定线程。
    finished属性中的值更改为YES时,将执行completionBlockcompletionBlock应该用于通知或执行可能与操作的实际任务相关但不属于该操作的其他任务。
    在iOS 8后,在completionBlock开始执行后,此属性设置为nil

    7. addDependency、 removeDependency、dependencies
    - (void)addDependency:(NSOperation *)op;
    - (void)removeDependency:(NSOperation *)op;
    
    @property (readonly, copy) NSArray<NSOperation *> *dependencies;
    
    • addDependency:使当前操作的执行依赖于op的完成(取消操作同样将其标记为已完成)。在所有依赖操作完成执行之前,当前操作不被认为准备好执行(isReady)。 如果当前操作已经在执行其任务,则添加依赖项没有实际效果。 另外,不要写出循环依赖的代码。
    • removeDependency:删除对指定操作的依赖性。
    • dependencies:依赖的操作对象数组,在所有依赖操作完成执行之前,当前操作对象不得执行。 在依赖的操作完成执行时,不会从此dependencies中删除操作。 您可以使用此列表来跟踪所有的依赖操作,包括那些已经完成执行的操作。 从此列表中删除操作的唯一方法是使用removeDependency:方法。
    8. qualityOfService

    当前操作的优先级,默认值为NSQualityOfServiceBackground,修改后,此值会覆盖NSOperationQueue的优先级

    9. queuePriority

    表示操作队列中操作的执行相对优先级。 该值用于影响操作出列和执行的顺序。 默认NSOperationQueuePriorityNormal。不要使用queuePriority来实现不同操作对象之间的依赖关系管理(因为就算 操作A 的queuePriority高于操作 B,也不能保证执行完操作A 再执行操作 B)。 所以如果需要在操作之间建立依赖关系,请使用addDependency:方法。

    10. waitUntilFinished

    阻止当前线程的执行,直到操作对象完成其任务。因为会阻塞当前线程,所以不要在主线程调用,更不要在当前操作的operationQueue上调用,这样会导致死锁。
    代码如下:

        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"block Operation before");
            [NSThread sleepForTimeInterval:2.];
            NSLog(@"block Operation end");
        }];
        [queue addOperation:blockOperation];
        dispatch_async(globalQueue, ^{
            NSLog(@"block Operation wait");
            [blockOperation waitUntilFinished];
            NSLog(@"block Operation done");
        });
    

    执行结果:

    2018-11-09 14:20:27.667928+0800 YMultiThreadDemo[1868:242200] block Operation wait
    2018-11-09 14:20:27.667934+0800 YMultiThreadDemo[1868:242199] block Operation before
    2018-11-09 14:20:29.672049+0800 YMultiThreadDemo[1868:242199] block Operation end
    2018-11-09 14:20:29.672413+0800 YMultiThreadDemo[1868:242200] block Operation done
    

    NSInvocationOperation

    - (instancetype)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
    

    target:定义sel的对象
    sel:运行操作时要调用的选择器。 可以采用0或1个参数; 如果它接受参数,则该参数的类型必须为id。 方法的返回类型可以是void,或可以作为id类型返回的对象。返回值通过类的result属性获取。
    arg:要传递给sel的参数对象。 如果没有参数,设置为nil
    下面的代码有参数,有返回值,因为获取返回值需确定操作已经执行完成,所以使用waitUntilFinished方法:

        NSString *string = @"abcdefg";
        NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(changeString:) object:string];
        [queue addOperation:invocationOperation];
        dispatch_async(globalQueue, ^{
            [invocationOperation waitUntilFinished];
            NSLog(@"invocation Operation result:%@",invocationOperation.result);
        });
    - (NSString *)changeString:(NSString *)string{
        NSLog(@"changeString before");
        [NSThread sleepForTimeInterval:1.];
        NSLog(@"changeString end");
        return string.capitalizedString;
    }
    

    执行结果:

    2018-11-09 15:07:32.062420+0800 YMultiThreadDemo[2305:292761] changeString before
    2018-11-09 15:07:33.066702+0800 YMultiThreadDemo[2305:292761] changeString end
    2018-11-09 15:07:33.067063+0800 YMultiThreadDemo[2305:292759] invocation Operation result:Abcdefg
    

    NSBlockOperation

    NSBlockOperation类是NSOperation的具体子类,它管理一个或多个块的并发执行。 您可以使用此对象一次执行多个块,而无需为每个块创建单独的操作对象。 当执行多个块时,仅当所有块都已完成执行时,才认为操作本身已完成。

    + (instancetype)blockOperationWithBlock:(void (^)(void))block;
    
    - (void)addExecutionBlock:(void (^)(void))block;
    @property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
    

    blockOperationWithBlock:创建并返回NSBlockOperation对象,并将指定的block块添加到该对象。
    addExecutionBlock:将指定的block块添加到对象要执行的块列表中。在对象正在执行或已经完成时调用此方法会导致抛出NSInvalidArgumentException异常。
    测试代码:

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"block Operation before");
            [NSThread sleepForTimeInterval:2.];
            NSLog(@"block Operation end");
        }];
        [blockOperation addExecutionBlock:^{
            NSLog(@"block2 Operation before");
            [NSThread sleepForTimeInterval:1.];
            NSLog(@"block2 Operation end");
        }];
        [blockOperation addExecutionBlock:^{
            NSLog(@"block3 Operation before");
            [NSThread sleepForTimeInterval:3.];
            NSLog(@"block3 Operation end");
        }];
        [queue addOperation:blockOperation];
        
        dispatch_async(globalQueue, ^{
            NSLog(@"block Operation wait");
            [blockOperation waitUntilFinished];
            NSLog(@"block all Operation done");
        });
    

    执行结果如下,可以看出blockOperation是并行的,且所有block执行完成才算blockOperation完成

    2018-11-09 15:31:35.807683+0800 YMultiThreadDemo[2563:321503] block Operation wait
    2018-11-09 15:31:35.807760+0800 YMultiThreadDemo[2563:321504] block Operation before
    2018-11-09 15:31:35.807807+0800 YMultiThreadDemo[2563:321506] block2 Operation before
    2018-11-09 15:31:35.807825+0800 YMultiThreadDemo[2563:321505] block3 Operation before
    2018-11-09 15:31:36.809055+0800 YMultiThreadDemo[2563:321506] block2 Operation end
    2018-11-09 15:31:37.809152+0800 YMultiThreadDemo[2563:321504] block Operation end
    2018-11-09 15:31:38.812710+0800 YMultiThreadDemo[2563:321505] block3 Operation end
    2018-11-09 15:31:38.812916+0800 YMultiThreadDemo[2563:321503] block all Operation done
    

    参考文献

    https://developer.apple.com/documentation/foundation/nsoperation?language=objc

    https://developer.apple.com/documentation/foundation/nsoperationqueue?language=objc

    相关文章

      网友评论

          本文标题:iOS多线程之NSOperationQueue、NSOperat

          本文链接:https://www.haomeiwen.com/subject/rsyuxqtx.html