NSOperation中的依赖项何时会调用completionBlock [英] When will completionBlock be called for dependencies in NSOperation

查看:88
本文介绍了NSOperation中的依赖项何时会调用completionBlock的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从文档中

当isFinished方法返回的值更改为YES时,将执行您提供的完成块.因此,该块是在操作的主要任务完成或取消后由操作对象执行的.

The completion block you provide is executed when the value returned by the isFinished method changes to YES. Thus, this block is executed by the operation object after the operation’s primary task is finished or cancelled.

如果要这么做,我正在使用RestKit/AFNetworking.

I'm using RestKit/AFNetworking, if that matters.

我在OperationQueueNSOperation中具有多个依赖项.我使用完成块来设置孩子需要的一些变量(将结果追加到数组中).

I have multiple dependencies in my NSOperation in a OperationQueue. I use the completion block to set some variables (appending the results to an array) that my child requires.

(task1,...,taskN)-> taskA

(task1,...,taskN) -> taskA

taskA addDependency:task1-taskN

taskA addDependency: task1-taskN

由于孩子可以在触发完成块之前执行,因此taskA会收到不完整的数据吗?

Will taskA receive incomplete data since the child can execute before the completion block is fired?

参考

Reference

NSOperations及其完成块是否同时运行?

我通过在完成区块中添加睡眠进行了简单的测试,结果却有所不同.完成块在主线程中运行.当所有完成块都处于休眠状态时,子任务已运行.

I did a simple test by adding a sleep in my completion block and I had a different result. The completion block runs in the main thread. While all the completion block are sleeping, the child task ran.

推荐答案

正如我在下面的一些观察"中讨论的那样,您无法保证最终的从属操作不会在其他杂项AFNetworking完成块完成之前开始.令我惊讶的是,如果最后的操作确实需要等待这些完成块完成,那么您有两种选择:

As I discuss below under "a few observations", you have no assurances that this final dependent operation will not start before your other sundry AFNetworking completion blocks have finished. It strikes me that if this final operation really needs to wait for these completion blocks to finish, then you have a couple of alternatives:

  1. 在每个 n 完成块中使用信号量来指示何时完成,并让完成操作等待 n 信号;或

  1. Use semaphores within each of the n the completion blocks to signal when they're done and have the completion operation wait for n signals; or

不要将最后的操作排在队列中,而是让您的单个上传的完成块跟踪有多少未完成的上传仍未完成,何时降至零,然后启动最后的发布" 操作.

Don't queue this final operation up front, but rather have your completion blocks for the individual uploads keep track of how many pending uploads are still incomplete, and when it falls to zero, then initiate the final "post" operation.

正如您在评论中指出的那样,您可以将对AFNetworking操作及其完成处理程序的调用包装在自己的操作中,然后可以使用标准的addDependency机制.

As you pointed out in your comments, you could wrap your invocation of the AFNetworking operation and its completion handler in your own operation, at which point you can then use the standard addDependency mechanism.

您可以放弃addDependency方法(该操作所依赖的操作的isFinished键上添加一个观察者,一旦所有这些依赖关系都得到解决,就执行isReady KVN;问题是理论上可以在完成代码完成之前 发生),并用您自己的isReady逻辑替换它.例如,假设您有一个后期操作,您可以添加自己的键依赖性并在完成块中手动将其删除,而不是在isFinished时将其自动删除.因此,您可以自定义操作

You could abandon the addDependency approach (which adds an observer on the isFinished key of the operation upon which this operation is dependent, and once all those dependencies are resolved, performs the isReady KVN; the problem being that this can theoretically happen before your completion block is done) and replace it with your own isReady logic. For example, imagine you had a post operation which you could add your own key dependencies and remove them manually in your completion block, rather than having them removed automatically upon isFinished. Thus, you custom operation

@interface PostOperation ()
@property (nonatomic, getter = isReady) BOOL ready;
@property (nonatomic, strong) NSMutableArray *keys;
@end

@implementation PostOperation

@synthesize ready = _ready;

- (void)addKeyDependency:(id)key {
    if (!self.keys)
        self.keys = [NSMutableArray arrayWithObject:key];
    else
        [self.keys addObject:key];

    self.ready = NO;
}

- (void)removeKeyDependency:(id)key {
    [self.keys removeObject:key];

    if ([self.keys count] == 0)
        self.ready = YES;
}

- (void)setReady:(BOOL)ready {
    if (ready != _ready) {
        [self willChangeValueForKey:@"isReady"];
        _ready = ready;
        [self didChangeValueForKey:@"isReady"];
    }
}

- (void)addDependency:(NSOperation *)operation{
    NSAssert(FALSE, @"You should not use addDependency with this custom operation");
}

然后,您的应用程序代码可以使用addKeyDependency而不是addDependency进行类似的操作,并在完成代码块中显式地使用removeKeyDependencycancel:

Then, your app code could do something like, using addKeyDependency rather than addDependency, and explicitly either removeKeyDependency or cancel in the completion blocks:

PostOperation *postOperation = [[PostOperation alloc] init];

for (NSInteger i = 0; i < numberOfImages; i++) {
    NSURL *url = ...
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSString *key = [url absoluteString]; // or you could use whatever unique value you want

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        // update your model or do whatever

        // now inform the post operation that this operation is done

        [postOperation removeKeyDependency:key];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // handle the error any way you want

        // perhaps you want to cancel the postOperation; you'd either cancel it or remove the dependency

        [postOperation cancel];
    }];
    [postOperation addKeyDependency:key];
    [queue addOperation:operation];
}

[queue addOperation:postOperation];

这是使用AFHTTPRequestOperation的方法,显然您可以使用适合于上载的AFNetworking操作替换所有逻辑,但是希望它可以说明这一想法.

This is using AFHTTPRequestOperation, and you'd obviously replace all of this logic with the appropriate AFNetworking operation for your upload, but hopefully it illustrates the idea.


原始答案:

一些观察结果:


Original answer:

A few observations:

    正如我认为您得出的结论,
  1. 当操作完成时,它(a)启动其完成块; (b)使该队列可用于其他操作(由于maxConcurrentOperationCount而尚未启动的操作,或由于这些操作之间的依赖性而尚未启动的操作).我不认为您有任何保证可以在下一个操作开始之前完成该完成工作.

  1. As I think you concluded, when your operation completes, it (a) initiates its completion block; (b) makes the queue available for other operations (either operations that had not yet started because of maxConcurrentOperationCount, or because of dependencies between the operations). I do not believe that you have any assurances that the completion block will be done before that next operation commences.

根据经验,在完成完成块之后,似乎依赖操作实际上不会触发,但是(a)我在任何地方都看不到该文档,并且(b)这没什么意思,因为如果您使用的是AFNetworking自己的setCompletionBlockWithSuccess,它最终以异步方式将块分配到主队列(或定义的successCallbackQueue),从而破坏了所有(未记录的)同步保证.

Empirically, it looks like the dependent operation does not actually trigger until after the completion blocks are done, but (a) I don't see that documented anywhere and (b) this is moot because if you're using AFNetworking's own setCompletionBlockWithSuccess, it ends up dispatching the block asynchronously to the main queue (or the defined successCallbackQueue), thereby thwarting any (undocumented) assurances of synchrony.

此外,您说完成块在主线程中运行.如果您在谈论内置的NSOperation完成块,则没有这样的保证.实际上,setCompletionBlock

Furthermore, you say that the completion block runs in the main thread. If you're talking about the built in NSOperation completion block, you have no such assurances. In fact, the setCompletionBlock documentation says:

不能保证您的完成块的确切执行上下文,但通常是辅助线程.因此,您不应使用此块来执行任何需要非常特定的执行上下文的工作.相反,您应该将这项工作分流到应用程序的主线程或有能力执行此任务的特定线程.例如,如果您有一个自定义线程来协调操作的完成,则可以使用完成块来对该线程执行ping操作.

The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it. For example, if you have a custom thread for coordinating the completion of the operation, you could use the completion block to ping that thread.

但是,如果您要谈论的是AFNetworking的自定义完成功能块之一,例如您可能会使用AFHTTPRequestOperationsetCompletionBlockWithSuccess设置的那些,那么,是的,确实,这些通常被分派回主队列.但是AFNetworking使用标准的completionBlock机制来执行此操作,因此上述问题仍然适用.

But if you're talking about one of AFNetworking's custom completion blocks, e.g. those that you might set with AFHTTPRequestOperation's setCompletionBlockWithSuccess, then, yes, it's true that those are generally dispatched back to the main queue. But AFNetworking does this using the standard completionBlock mechanism, so the above concerns still apply.

这篇关于NSOperation中的依赖项何时会调用completionBlock的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆