美文网首页
Concurrent Operations

Concurrent Operations

作者: woshishui1243 | 来源:发表于2018-05-22 15:34 被阅读5次

    如果你有一个需要耗时很长的操作,你可以创建NSOperation的子类,然后将耗时代码放在main函数中。

    @implementation CalculateOperation
    
    - (void)main{
        // 耗时任务
    }
    
    @end
    

    执行这个任务,你只需要将他添加到NSOperationQueue中:

    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    NSOperation *calOperation = [[CalculateOperation alloc] init];
    [queue addOperation:calOperation];
    

    如果将多个operation加入到queue中,他们会在后台线程中并发执行,从而不影响主线程中的用户交互。queue会将根据CPU内核的数量智能地调度并行操作的数量,从而有效地利用用户的硬件。

    唯一需要注意的是一个operation的生命周期就是main方法。一旦该方法返回,operation就会置为finished,并从queue中移除。如果你需要在main函数中使用具有异步API的类,必须通过一些特殊处理才能达到目的。通常,您必须使用runloop来确保main函数不会过早返回。

    你可以通过在NSOperation的子类中重写isConcurrent方法,并返回YES,创建了一个异步operation:

    - (BOOL)isConcurrent {
        return YES;
    }
    

    但是在10.6系统以后,这个方法不再准确。在10.6以后的系统中start方法总是在后台线程中被调用。为了只使用主线程以及依赖于runloop的异步API来正常工作,我们需要将我们的工作分流到主线程。

    在任何情况下,并发线程的另一个主要区别是重写start方法,而不是重写main方法。此外,start方法返回后,operation并不是finished状态。这就允许您控制operation的生命周期。
    当处理异步API时,可以通过在主线程上执行start方法,在start方法中执行异步调用,保证operation一直处于running状态直到异步调用完成。

    不过这样我们需要做一些额外的操作。我们需要跟踪isExecutingisFinished,我们需要通过KVC的形式来修改它们。通常情况下,我们使用实例变量来实现这一点。只有当isFinished属性更改为YES时,才认为该操作已完成。

    例如,如果我们想使用NSURLConnection从URL下载数据的操作,它的初始化器将是:

    - (id)initWithUrl:(NSURL *)url {
        self = [super init];
        if (self == nil)
            return nil;
        
        _url = [url copy];
        _isExecuting = NO;
        _isFinished = NO;
        
        return self;
    }
    
    - (void)start {
        if (![NSThread isMainThread])  {
            [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
            return;
        }
    
        NSLog(@"opeartion for <%@> started.", _url);
        
        [self willChangeValueForKey:@"isExecuting"];
        _isExecuting = YES;
        [self didChangeValueForKey:@"isExecuting"];
    
        NSURLRequest * request = [NSURLRequest requestWithURL:_url];
        _connection = [[NSURLConnection alloc] initWithRequest:request
                                                      delegate:self];
        if (_connection == nil)
            [self finish];
    }
    

    其中有3点很重要:
    1、我们必须确保任务在主线程上执行。
    2、必须修改isExecutingYES
    3、start方法在NSURLConnection完成之前返回,但是operation必须保持在执行状态。这就意味着,operation始终处在queue中,而不需要通过runloop。
    最后,自定义finish方法来结束operation:

    - (void)finish {
        NSLog(@"operation for <%@> finished. "
              @"status code: %d, error: %@, data size: %u",
              _url, _statusCode, _error, [_data length]);
        
        _connection = nil;
        
        [self willChangeValueForKey:@"isExecuting"];
        [self willChangeValueForKey:@"isFinished"];
    
        _isExecuting = NO;
        _isFinished = YES;
    
        [self didChangeValueForKey:@"isExecuting"];
        [self didChangeValueForKey:@"isFinished"];
    }
    

    这里的关键点是我们改变isExecutingisFinished。只有他们被分别设置为NOYES时,操作才会从队列中删除。队列使用KVO管理这些属性值。
    NSURLConnection 的代理方法中正确的结束operation:

    - (void)connection:(NSURLConnection *)connection
    didReceiveResponse:(NSURLResponse *)response {
        _data = [[NSMutableData alloc] init];
        NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
        _statusCode = [httpResponse statusCode];
    }
    
    - (void)connection:(NSURLConnection *)connection
        didReceiveData:(NSData *)data {
        [_data appendData:data];
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        [self finish];
    }
    
    - (void)connection:(NSURLConnection *)connection
      didFailWithError:(NSError *)error {
        _error = [error copy];
        [self finish];
    }
    

    我们不必把异步API转换成同步API,但是我们仍然能够将这个任务打包为一个操作。虽然使用操作似乎有点违反直觉,但它确实有它的好处。例如,可以使用队列将并行下载的数量限制为两个:

        _queue = [[NSOperationQueue alloc] init];
        [_queue setMaxConcurrentOperationCount:2];
    

    而且,还可以利用操作的依赖关系来保证任务以正确的顺序执行。

    相关文章

      网友评论

          本文标题:Concurrent Operations

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