Ios 保留NSOperationQueue,直到上一个操作完成
我想执行一些操作,只需要在完成前一个操作后才开始下一个操作。我添加的操作将向服务器发送异步调用并接收数据。我只想在对服务器的第一次调用完成从服务器接收数据后才开始下一个操作。怎么做Ios 保留NSOperationQueue,直到上一个操作完成,ios,objective-c,nsoperationqueue,Ios,Objective C,Nsoperationqueue,我想执行一些操作,只需要在完成前一个操作后才开始下一个操作。我添加的操作将向服务器发送异步调用并接收数据。我只想在对服务器的第一次调用完成从服务器接收数据后才开始下一个操作。怎么做 {.... PhotoDownloader *pd = [[PhotoDownloader alloc] init]; [GetGlobalOperationQueue addOperation:pd]; } 在PhotoDownloader中,我将分配所需的参数并调用一个全局函数来处理所有
{....
PhotoDownloader *pd = [[PhotoDownloader alloc] init];
[GetGlobalOperationQueue addOperation:pd];
}
在PhotoDownloader中,我将分配所需的参数并调用一个全局函数来处理所有请求
[GlobalCommunicationUtil sendServerReq:reqObj withResponseHandler:self];
在sendServerReq方法中,我将构造URL请求并将其发送到服务器,此调用是“sendAsynchronousRequest”调用。PhotoDownloader将具有CommunicationUtil的委托方法。是否使用NSOperationQueue进行管理 这种行为很容易在串行队列中实现 假设您有一个类来管理这些操作,您将在init方法中使用
queue = dispatch_queue_create("com.example.MyQueue", NULL);
您将有一个方法将请求排队,类似这样
- (void) enqueueRequest:(NSURL *)requestURL
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(queue, ^{ /* get data from requestURL */ }) });
}
这样,一次只有一个请求处于活动状态,即使每个请求将在单独的后台线程中执行,并且在活动请求完成之前,几个请求将排队。此问题分为两个部分:
NSOperationQueue
,只需将maxConcurrentOperationCount
设置为1
即可实现这一点
或者,更灵活一点,您可以在需要依赖关系的操作之间建立依赖关系,但也可以享受并发性。例如,如果要根据第三个请求的完成情况发出两个网络请求,可以执行以下操作:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4; // generally with network requests, you don't want to exceed 4 or 5 concurrent operations;
// it doesn't matter too much here, since there are only 3 operations, but don't
// try to run more than 4 or 5 network requests at the same time
NSOperation *operation1 = [[NetworkOperation alloc] initWithRequest:request1 completionHandler:^(NSData *data, NSError *error) {
[self doSomethingWithData:data fromRequest:request1 error:error];
}];
NSOperation *operation2 = [[NetworkOperation alloc] initWithRequest:request2 completionHandler:^(NSData *data, NSError *error) {
[self doSomethingWithData:data fromRequest:request2 error:error];
}];
NSOperation *operation3 = [[NetworkOperation alloc] initWithRequest:request3 completionHandler:^(NSData *data, NSError *error) {
[self doSomethingWithData:data fromRequest:request3 error:error];
}];
[operation2 addDependency:operation1]; // don't start operation2 or 3 until operation1 is done
[operation3 addDependency:operation1];
[queue addOperation:operation1]; // now add all three to the queue
[queue addOperation:operation2];
[queue addOperation:operation3];
NSOperation
子类
“异步”NSOperation
只是在发出isFinished
通知之前无法完成的操作(从而允许它启动的任何异步任务完成)。而NSOperation
类只需在其isAsynchronous
实现中返回YES
即可将自己指定为异步操作。因此,异步操作的抽象类实现可能如下所示:
// AsynchronousOperation.h
@import Foundation;
@interface AsynchronousOperation : NSOperation
/**
Complete the asynchronous operation.
If you create an asynchronous operation, you _must_ call this for all paths of execution
or else the operation will not terminate (and dependent operations and/or available
concurrent threads for the operation queue (`maxConcurrentOperationCount`) will be blocked.
*/
- (void)completeOperation;
@end
及
现在我们有了抽象的、异步的NSOperation
子类,我们可以在具体的NetworkOperation
类中使用它:
#import "AsynchronousOperation.h"
NS_ASSUME_NONNULL_BEGIN
typedef void(^NetworkOperationCompletionBlock)(NSData * _Nullable data, NSError * _Nullable error);
@interface NetworkOperation : AsynchronousOperation
@property (nullable, nonatomic, copy) NetworkOperationCompletionBlock networkOperationCompletionBlock;
@property (nonatomic, copy) NSURLRequest *request;
- (instancetype)initWithRequest:(NSURLRequest *)request completionHandler:(NetworkOperationCompletionBlock)completionHandler;
@end
NS_ASSUME_NONNULL_END
及
现在,在这个例子中,我使用了这些异步网络请求的基于块的实现,但是这个想法在基于代理的连接/会话中也同样有效。(唯一的麻烦是NSURLSession
将其任务相关的委托方法指定为会话的一部分,而不是网络任务。)
显然,您自己的NetworkOperation
类的实现可能会有很大的不同(使用委托模式或完成块模式等),但希望这能说明并发操作的思想。有关更多信息,请参阅《并发编程指南》的一章,特别是标题为“为并发执行配置操作”的部分异步操作的swift版本(不太明显): 要序列化操作,请执行以下操作:
let operationQueue = OperationQueue()
let operation1 = NetworkOperation()
let operation2 = NetworkOperation()
operation2.addDependency(operation1)
operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)
您能告诉我们您当前如何向服务器发送请求吗?是的,网络请求将是同步的,以保持简单,dispatch_async调用将负责启动新线程以避免阻塞主线程。您好,您的第二个解释回答了我的问题。是的,我正在使用“sendAsynchronousRequest”,如果我正在使用参数从操作调用全局/util方法,并且该全局/util方法正在发送异步请求,并且它已经有一个完成块,我怀疑如何处理。我是用另一种方式做的。我将操作队列作为属性分配给操作本身,第一行将挂起操作队列。异步请求完成并调用操作中的委托后,我将恢复操作队列。正如我所希望的,有什么缺点吗?您肯定不想挂起队列或类似的东西(因为您可能(最终)同时运行多个请求)。我可能会在这个全局util方法中重复完成块模式(请参见的第一部分),然后在您提供给这个全局util方法的完成处理程序中提供
completeOperation
。或者您可以使用sendSynchronousRequest
,整个问题就会解决(但请确保您从未从主队列调用该方法)。如果您需要其他帮助,请使用相关的代码示例更新您的问题。我已经包含了代码片段。你现在能指导我吗?@Arock在sendServerReq
中,你说你在使用sendsynchronousrequest
。但是,从代码片段中不清楚sendServerReq
如何通知调用方下载完成。您有一个带有responseHandler
,但是:我是否推断您有某种协议,sendServerReq
将通过该协议通知responseHandler
下载完成?因此,您将有main
(在我的代码片段中)调用sendServerReq
,但在完成请求的委托方法中,将调用completeOperation
。
// NetworkOperation.m
#import "NetworkOperation.h"
@interface NetworkOperation ()
@property (nonatomic, weak) NSURLSessionTask *task;
@end
@implementation NetworkOperation
- (instancetype)initWithRequest:(NSURLRequest *)request completionHandler:(NetworkOperationCompletionBlock)completionHandler {
self = [self init];
if (self) {
self.request = request;
self.networkOperationCompletionBlock = completionHandler;
}
return self;
}
- (void)main {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithRequest:self.request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (self.networkOperationCompletionBlock) {
self.networkOperationCompletionBlock(data, error);
self.networkOperationCompletionBlock = nil;
}
[self completeOperation];
}];
[task resume];
self.task = task;
}
- (void)cancel {
[super cancel];
[self.task cancel];
}
@end
final class NetworkOperation: Operation {
lazy var session: NSURLSession = {
return NSURLSession.sharedSession()
}()
private var _finished = false {
willSet {
willChangeValue(forKey: "isFinished")
}
didSet {
didChangeValue(forKey: "isFinished")
}
}
private var _executing = false {
willSet {
willChangeValue(forKey: "isExecuting")
}
didSet {
didChangeValue(forKey: "isExecuting")
}
}
override var isAsynchronous: Bool {
return true
}
override var isFinished: Bool {
return _finished
}
override var isExecuting: Bool {
return _executing
}
override func start() {
_executing = true
execute()
}
func execute() {
task = session.downloadTaskWithURL(NSURL(string: "yourURL")!) {
(url, response, error) in
if error == nil {
// Notify the response by means of a closure or what you prefer
// Remember to run in the main thread since NSURLSession runs its
// task on background by default
} else {
// Notify the failure by means of a closure or what you prefer
// Remember to run in the main thread since NSURLSession runs its
// task on background by default
}
// Remember to tell the operation queue that the execution has completed
self.finish()
}
}
func finish() {
//Async task complete and hence the operation is complete
_executing = false
_finished = true
}
}
let operationQueue = OperationQueue()
let operation1 = NetworkOperation()
let operation2 = NetworkOperation()
operation2.addDependency(operation1)
operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)