[ios]NSOperation + setCompletionBlock

发布时间: 2017/3/23 2:07:33
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

几个不同的问题对 NSOperationNSOperationQueue 和我知道伙计们,你的答案会帮助我;

我要加载大量的图像和我已经创建了基于我自己装载机 NSOperationNSOperationQueueNSURLConnection (异步加载);

问题︰

  1. 如果我设置 maxConcurrentOperationCount (例如 3) 队列 ( NSOperationQueue ),它意味着只有 3 在同一时间甚至队列执行的操作有 100 例手术吗?

  2. 当我设置属性 maxConcurrentOperationCount 队列有时" setCompletionBlock "不是和计数 ( operationCount ) 只会增加;为什么呢?

MyLoader:

- (id)init
{
    self = [super init];
    if (self) {
        _loadingFiles = [NSMutableDictionary new];
        _downloadQueue = [NSOperationQueue new];
        _downloadQueue.maxConcurrentOperationCount = 3;
        _downloadQueue.name = @"LOADER QUEUE";

    }
    return self;
}

- (void)loadFile:(NSString *)fileServerUrl handler:(GetFileDataHandler)handler {
    if (fileServerUrl.length == 0) {
        return;
    }

    if ([_loadingFiles objectForKey:fileServerUrl] == nil) {
        [_loadingFiles setObject:fileServerUrl forKey:fileServerUrl];

        __weak NSMutableDictionary *_loadingFiles_ = _loadingFiles;
        MyLoadOperation *operation = [MyLoadOperation new];
        [operation fileServerUrl:fileServerUrl handler:^(NSData *fileData) {
            [_loadingFiles_ removeObjectForKey:fileServerUrl];
            if (fileData != nil) {
                handler(fileData);
            }
        }];
        [operation setQueuePriority:NSOperationQueuePriorityLow];
        [_downloadQueue addOperation:operation];

        __weak NSOperationQueue *_downloadQueue_ = _downloadQueue;
        [operation setCompletionBlock:^{
            NSLog(@"completion block :%i", _downloadQueue_.operationCount);
        }];
    }
}

纱布︰

@interface MyLoadOperation()
@property (nonatomic, assign, getter=isOperationStarted) BOOL operationStarted;

@property(nonatomic, strong)NSString *fileServerUrl;

@property(nonatomic, copy)void (^OnFinishLoading)(NSData *);

@end
@implementation MyLoadOperation
- (id)init
{
    self = [super init];
    if (self) {
        _executing = NO;
        _finished = NO;
    }
    return self;
}
- (void)fileServerUrl:(NSString *)fileServerUrl
              handler:(void(^)(NSData *))handler {

    @autoreleasepool {

        self.fileServerUrl = fileServerUrl;

        [self setOnFinishLoading:^(NSData *loadData) {
            handler(loadData);
        }];

        [self setOnFailedLoading:^{
            handler(nil);
        }];
        self.url = [[NSURL alloc] initWithString:self.fileServerUrl];
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
                                        initWithURL:self.url
                                        cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                        timeoutInterval:25];
        [request setValue:@"" forHTTPHeaderField:@"Accept-Encoding"];

        self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];

        [self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        [self.connection start];
        _data = [[NSMutableData alloc] init];

    }
}
- (void)main {
    @autoreleasepool {
        [self stop];
    }
}
- (void)start {
    [self setOperationStarted:YES];

    [self willChangeValueForKey:@"isFinished"];
    _finished = NO;
    [self didChangeValueForKey:@"isFinished"];
    if ([self isCancelled])
    {
        [self willChangeValueForKey:@"isFinished"];
        _finished = YES;
        _executing = NO;
        [self didChangeValueForKey:@"isFinished"];
    }
    else
    {
        [self willChangeValueForKey:@"isExecuting"];
        _finished = NO;
        _executing = YES;
        [self didChangeValueForKey:@"isExecuting"];
    }
}

- (BOOL)isConcurrent {

    return YES;
}

- (BOOL)isExecuting {
    return _executing;
}

- (BOOL)isFinished {

    return _finished;
}

- (void)cancel {
    [self.connection cancel];
    if ([self isExecuting])
    {
        [self stop];
    }
    [super cancel];
}
#pragma mark -NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if ([self OnFinishLoading]) {
        [self OnFinishLoading](_data);
    }
    if (![self isCancelled]) {
        [self stop];

    }
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
;
    if (![self isCancelled]) {
        [self stop];
    }
}
- (void)stop {
    @try {
        __weak MyLoadOperation *self_ = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            [self_ completeOperation];
        });
    }
    @catch (NSException *exception) {
        NSLog(@"Exception! %@", exception);
        [self completeOperation];
    }
}
- (void)completeOperation {
    if (![self isOperationStarted]) return;

    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
    _executing = NO;
    _finished  = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

解决方法 1:

必须启动该连接操作的 start 方法,并不在 fileServerUrl:handler:

我一共,删除此方法,只为 init 方法提供所有必需的参数,在那里你可以完全安装操作。然后,在方法 start 启动连接。

此外,尚不清楚为什么你重写 main

修改状态变量 _executing_finished 可以更简洁、 更清晰 (你不需要设置它们最初,因为是已经初始化为 NO )。只有在"最后"的方法中设置他们 completeOperation 包括志愿通知。

你也不需要在 @try/@catch stop ,因为函数 dispatch_async() 不引发objective-c 异常。

cancel 方法不是线程安全的还有其他的几个问题。我会建议进行以下更改︰

@implementation MyOperation {
    BOOL _executing;
    BOOL _finished;

    NSError* _error;  // remember the error
    id _result;       // the "result" of the connection, unless failed
    completion_block_t _completionHandler; //(your own completion handler)
    id _self; // strong reference to self
}

// Use the "main thread" as the "synchronization queue"

- (void) start
{
    // Ensure start will be called only *once*:
    dispatch_async(dispatch_get_main_queue(), ^{
        if (!self.isCancelled && !_finished && !_executing) {

            [self willChangeValueForKey:@"isExecuting"];
            _executing = YES;
            [self didChangeValueForKey:@"isExecuting"];
            _self = self; // keep a strong reference to self in order to make 
                          // the operation "immortal for the duration of the task

            // Setup connection:
            ...

            [self.connection start];
        }
    });
}

- (void) cancel 
{
    dispatch_async(dispatch_get_main_queue, ^{
        [super cancel];
        [self.connection cancel];
        if (!_finished && !_executing) {
            // if the op has been cancelled before we started the connection
            // ensure the op will be orderly terminated:
            self.error = [[NSError alloc] initWithDomain:@"MyOperation"
                                                    code:-1000
                                                userInfo:@{NSLocalizedDescriptionKey: @"cancelled"}];
            [self completeOperation];
        }
    });
}


- (void)completeOperation 
{
    [self willChangeValueForKey:@"isExecuting"];
    self.isExecuting = NO;
    [self didChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];
    self.isFinished = YES;
    [self didChangeValueForKey:@"isFinished"];

    completion_block_t completionHandler = _completionHandler;
    _completionHandler = nil;
    id result = self.result;
    NSError* error = self.error;
    _self = nil;
    if (completionHandler) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            completionHandler(result, error);
        });
    }
}


- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if ([self onFinishLoading]) {
        [self onFinishLoading](self.result);
    }
    [self completeOperation];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    if (self.error == nil) {
        self.error = error;
    }
    [self completeOperation];
}
官方微信
官方QQ群
31647020