Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios NSO操作+;setCompletionBlock_Ios_Objective C_Nsurlconnection_Nsoperation_Nsoperationqueue - Fatal编程技术网

Ios NSO操作+;setCompletionBlock

Ios NSO操作+;setCompletionBlock,ios,objective-c,nsurlconnection,nsoperation,nsoperationqueue,Ios,Objective C,Nsurlconnection,Nsoperation,Nsoperationqueue,关于NSOperation和NSOperationQueue我有一些不同的问题,我知道你们的答案会对我有所帮助 我必须加载大量图像,并基于NSOperation、NSOperationQueue和NSURLConnection(异步加载)创建了自己的加载程序 问题: 如果我为队列(NSOperationQueue)设置了maxConcurrentOperationCount(例如3),这是否意味着同一时间仅执行3个操作,即使队列有100个操作 当我为队列设置属性maxConcurrentOper

关于
NSOperation
NSOperationQueue
我有一些不同的问题,我知道你们的答案会对我有所帮助

我必须加载大量图像,并基于
NSOperation
NSOperationQueue
NSURLConnection
(异步加载)创建了自己的加载程序

问题:

  • 如果我为队列(
    NSOperationQueue
    )设置了
    maxConcurrentOperationCount
    (例如3),这是否意味着同一时间仅执行3个操作,即使队列有100个操作

  • 当我为队列设置属性
    maxConcurrentOperationCount
    时,有时“
    setCompletionBlock
    ”不起作用,计数(
    operationCount
    )只会增加;为什么?

  • 我的装载机:

    - (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"];
    }
    

    对于第一个问题,答案是肯定的,如果将3设置为最大操作数,则只能同时运行3个。

    第二个问题有点奇怪,我不能完全确定这个答案是否正确。
    当您将操作留给NSOperationQueue时,您无法确定它们将在哪个线程上执行,这会给异步连接带来一个巨大的问题。
    当您像往常一样启动NSURLConnection时,您会毫无问题地收到委托回调,这是因为连接运行在具有活动运行循环的线程上。如果在辅助线程上启动连接,将在该线程上调用回调,但如果不保持运行循环的活动状态,则将永远不会收到回调。
    这就是我的答案可能不正确的地方,GCD应该处理活动运行循环,因为GCD队列在活动线程上运行
    但如果不是,问题可能是操作在不同的线程上启动,调用start方法,但从不调用回调。尝试检查线程是否始终是主线程。

    在回答您的问题时:

  • 是的,
    maxConcurrentOperationCount
    为三表示一次只运行三个。像这样执行网络请求是您希望使用
    maxConcurrentOperationCount
    的最佳示例,因为如果不这样做,将导致尝试运行的网络请求过多,很可能会导致某些连接在使用较慢的网络连接时失败

  • 不过,这里的主要问题是,您正在从
    MyLoader
    调用操作的
    fileServerUrl
    方法(启动连接)。您已将请求从操作的
    开始
    断开(违背了
    3
    maxConcurrentCount
    的目的,可能会混淆操作的状态)

    start
    方法应启动连接(即,在三个可用并发操作之一可用之前不要启动请求)。此外,由于无法将URL和
    处理程序
    传递给
    start
    方法,因此应将保存这些值的逻辑移动到
    init
    方法的自定义格式副本中

  • 我们可能会建议对您的操作进行其他小的编辑(
    main
    不需要,
    operationStarted
    有点多余,简化
    \u执行
    /
    \u完成
    处理等),但关键问题是在
    fileServerUrl
    中启动连接,而不是由
    start
    方法启动

    因此:


    您必须在操作的
    start
    方法中启动连接,而不是在
    fileServerUrl:handler:
    中启动连接

    我将完全删除这个方法,只提供一个init方法,其中包含所有必需的参数,您可以完全设置操作。然后,在方法
    start
    中启动连接

    此外,不清楚为什么要覆盖
    main

    修改状态变量
    \u executing
    \u finished
    可以更简洁、更清晰(您不需要最初设置它们,因为它们已经初始化为
    NO
    )。仅在“最终”方法
    completeOperation
    中设置它们,包括KVO通知

    您也不需要在
    stop
    中使用@try/@catch,因为函数
    dispatch\u 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];
    }
    

    要明确的是,您是说当使用3的
    maxConcurrentOperationCount
    时,您看到所有100个操作都运行并完成了(不仅仅是前三个),但没有看到
    operationCount
    根本没有变化?
    operationCount
    方法并不完全可靠(请参阅该方法的文档),但您应该看到返回的值有一些变化。你能更详细地描述一下这种行为吗?谢谢你的回答;是的,你是对的,我必须更好地描述这种行为;看,如果我删除
    maxConcurrentOperationCount
    上面显示的代码,一切都很好;精细-我的意思是每次调用
    setCompletionBlock
    operationCount
    减少;如果我使用
    maxConcurrentOperationCount
    (数字无关紧要)
    setCompletionBlock
    调用,但它通常在我滚动
    表格视图时停止工作(我在其中显示加载图像);换句话说,我只更改一行代码,而代码更改行为;OP正在计划
    [nsrunlop main runloop]
    上的
    NSURLConnection
    委托,这解决了您描述的问题。我认为问题出在别处。谢谢你的回答;你给了我非常有用的建议;不幸的是,现在最大的问题是我无法停止执行操作。换句话说,方法
    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];
    }