Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/solr/3.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 保留NSOperationQueue,直到上一个操作完成_Ios_Objective C_Nsoperationqueue - Fatal编程技术网

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)