【问题标题】:NSOperationQueue with synchronous NSURLConnection具有同步 NSURLConnection 的 NSOperationQueue
【发布时间】:2013-11-27 15:44:59
【问题描述】:

基于earlier 问题: 我有一个看起来像这样的NSOperationQueue

NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        [someObject someSelector];
    }];

NSBlockOperation *block2= [NSBlockOperation blockOperationWithBlock:^{
    [someObject anotherSelector];
}];

[block2 addDependency:block1];
[queue addOperation:block1];
[queue addOperation:block2];

现在,在someSelector 里面我有:

returnData = [requesterObj getDataWithURL:(NSString*)url];

getDataWithURL 包含以下内容:

NSURL *requestUrl = [NSURL URLWithString:strUrl];

NSMutableURLRequest *request = [NSMutableURLRequest requestUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:timeout];
NSError *requestError;
NSURLResponse *urlResponse = nil;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];

现在,当我添加断点时,似乎在第一个块的 NSURLConnection 完成之前调用了第二个块。大概是因为对getDataWithURL 的调用本身是异步的。确保在该请求返回之前第一个块没有完成的最佳方法是什么。我应该尝试使用NSInvocation 将数据放入returnData 中吗?

【问题讨论】:

  • 这应该通过正确设置依赖关系来解决。在发布的代码中,您正在 开始 阻止操作而不设置依赖关系!那么 Assume that I have set up dependencies... 是什么意思?您可以在设置它们的位置发布相关代码吗?
  • 否,依赖项设置正确。问题是NSBlockOperation 无法知道异步请求尚未完成——它假定所有内容在块结束后都已完成。

标签: ios objective-c nsurlconnection nsoperationqueue


【解决方案1】:
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
    [someObject someSelector];
}];

这将创建一个“标准”块操作。在执行块后,此操作被认为已完成([block1 isFinished] 为真),即使从用户的角度来看异步网络操作尚未完成。

既然你不想要这个,你需要继承 NSOperation明确通过覆盖它的start 告诉它你的操作何时完成:

- (void)completeOperation {

    self.finished = YES;
    self.executing = NO;
}

- (void)start {

    if ([self isCancelled]) {
        [self completeOperation];
        return;
    }

    self.executing = YES;
    [self main];
    // this is where operation is set to finished in NSOperation 
}

- (void)main { 
    [someObject someSelectorWithCompletionBlock: ^() { 
        [self completeOperation]; 
        // retain cycle may exist unless completion block is destroyed afterwards
    }];
}

【讨论】:

    【解决方案2】:

    我个人更喜欢grand central dispatch,因为它使代码非常不言自明。此代码将依次调用两个选择器(从另一个线程),然后在主线程上调用doneSelector

    dispatch_async(dispatch_get_global_queue(0,0), ^{
        // These will be called in sequence on a background thread
        [someObject someSelector];
        [someObject anotherSelector]
    
        dispatch_async(dispatch_get_main_queue(), ^{
            // These will be called on the main thread after the above are done
            [someObject doneSelector];
            NSLog(@"Finished operation");
        });
    });
    

    【讨论】:

    • 如果作者正确地认为getDataWithURL 调用本身是异步的,则此GCD 示例还将在处理异步请求时执行[someObject anotherSelector]。事实上,这正是NSOperation 有帮助的地方!
    • [NSURLConnection sendSynchronousRequest:] 不是异步请求。该方法应该阻塞线程。
    • 是的,但根据作者的观察,我认为还有更多的异步内容正在发生(或者这句话具有误导性)。
    • 无论如何,同步调用根本不应该使用,它们会阻塞整个线程; GCD 会尽量减少使用的线程数,因此您可能会影响性能。
    【解决方案3】:

    如果它适用于您的情况,我会让事情变得更简单并执行以下操作。

    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        [someObject someSelector];
        [someObject anotherSelector];
    }];
    [queue addOperation:block1];
    

    【讨论】:

      【解决方案4】:

      当您第一次发布 your other question 时,我猜您可能一直在处理异步网络请求,如果您想要对异步网络请求进行操作,通常可以解决将此类网络请求包装在子类 NSOperation as I discussed in the answer to your other question 中的问题(正如 ilya 随后也在此处描述的那样)。

      您的问题表明您正在致电sendSynchronousRequest。如果这就是你所做的一切,那么NSOperation 子类化模式就不需要了,你应该能够毫无意外地使用addDependency。但是,您似乎发现getDataWithURL 本身就是将sendSynchronousRequest 提交到它自己的操作队列,从而有效地使其异步。您正确地确定,如果您从 getDataWithURL 中删除冗余操作队列逻辑,那么您的问题就解决了,不需要 NSOperation 子类化。

      但是,我不会很快放弃这个子类NSOperation 模式,因为sendSynchronousRequest 有一些限制,你可能会妨碍自己。也就是说,这些请求不能被取消。同样,您无法在这些请求正在进行时获得它们的进度,也无法处理任何高级功能,例如质询-响应身份验证等。您可能不需要任何这些功能,但由于这些限制,我们中的许多人通常会回避sendSynchronousRequest(以及sendAsynchronousRequest)。

      从长远来看,您可能会发现自己被基于NSURLConnectionDataDelegate 的网络调用所吸引,它可以解决这些限制。在这种情况下,这个NSOperation 子类模式可能会再次变得有用。或者,甚至更好的是,考虑使用像 AFNetworking 这样的框架,它提供基于 NSOperation 的网络解决方案,但为您做了很多繁琐的工作。

      【讨论】:

        猜你喜欢
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多