NSOperationQueue和后台支持

时间:2012-11-08 14:22:27

标签: ios background nsoperation nsoperationqueue

我在制定有用的策略以支持NSOperationQueue课程的背景方面遇到了一些问题。特别是,我有一堆NSOperation个执行以下操作:

  • 从网上下载文件
  • 解析文件
  • 在Core Data中导入数据文件

将操作插入到串行队列中。一旦操作完成,下一个操作就可以开始了。

我需要在应用进入后台时停止(或继续)操作。从这些讨论(Does AFNetworking have backgrounding support?Queue of NSOperations and handling application exit)中,我看到最好的方法是取消操作并在每个操作中使用isCancelled属性。然后,检查针对该属性的操作的关键点,它允许在应用程序进入后台时回滚执行(运行操作的)的状态。

基于突出背景支持的Apple模板,我该如何处理类似情况?我可以简单地取消操作或等待当前操作完成吗?有关详细信息,请参阅注释。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithExpirationHandler:^{

        // Do I have to call -cancelAllOperations or
        // -waitUntilAllOperationsAreFinished or both?

        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // What about here?

        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

提前谢谢。

修改

如果NSOperation main方法执行以下代码,如何按照响应取消事件模式进行操作?

- (void)main
{
    // 1- download

    // 2- parse
    // 2.1 read file location
    // 2.2 load into memory

    // 3- import
    // 3.1 fetch core data request
    // 3.2 if data is not present, insert it (or update)
    // 3.3 save data into persistent store coordinator
}

我描述的每个方法都包含各种步骤(非原子操作,下载除外)。因此,在这些步骤的每一步中都可能发生取消(以非预定义的方式)。我可以在每一步之前检查isCancelled属性吗?这有用吗?

根据Tammo Freese编辑2'编辑

我理解你的编辑代码是什么意思。但我担心的是以下几点。取消请求(用户可以按下主页按钮)可以在main执行中的任何时刻发生,因此,如果我只是返回,操作的状态将被破坏。在返回之前我需要清理它的状态吗?你觉得怎么样?

我描述的问题可能发生在我使用同步操作时(在他们运行的同一个线程中以同步方式执行的操作)。例如,如果main正在下载文件(通过+sendSynchronousRequest:returningResponse:error执行下载)并且应用程序被置于后台,那么它会发生什么?如何处理这种情况?

// download
if ([self isCancelled])
    return;

// downloading here <-- here the app is put in background

显然,我认为当应用程序放在前台时,操作会再次运行,因为它已被取消。换句话说,它被迫不能保持其状态。我错了吗?

1 个答案:

答案 0 :(得分:15)

如果我理解正确,你有一个NSOperationQueue,如果你的应用程序进入后台,你想

  1. 取消所有操作和
  2. 等待处理取消。
  3. 通常情况下这不应该花费太多时间,所以这应该足够了:

    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        [_queue cancelAllOperations];
        [_queue waitUntilAllOperationsAreFinished];
    }
    

    此处“太多时间”的定义大约是五秒钟:如果您阻止-applicationDidEnterBackground:超过该时间,您的应用将被终止并从内存中清除。

    假设完成取消的操作需要的时间超过5秒。然后你必须在后台等待(参见注释的解释):

    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            // If this block is called, our background time of normally 10 minutes
            // is almost exceeded. That would mean one of the cancelled operations
            // is not finished even 10 minutes after cancellation (!).
            // This should not happen.
            // What we do anyway is tell iOS that our background task has ended,
            // as otherwise our app will be killed instead of suspended.
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
    
        // Normally this one is fast, so we do it outside the asynchronous block.
        [_queue cancelAllOperations];
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // Wait until all the cancelled operations are finished.
            [_queue waitUntilAllOperationsAreFinished];
    
            // Dispatch to the main queue if bgTask is not atomic
            dispatch_async(dispatch_get_main_queue(), ^{
                [application endBackgroundTask:bgTask];
                bgTask = UIBackgroundTaskInvalid;
            });
        });
    }
    

    基本上我们告诉iOS我们需要一些时间来执行任务,当任务结束时,我们告诉iOS我们的任务已经结束。

    修改

    要回答编辑中的问题:要响应取消,只需检查取消,然后从-main方法返回。取消的操作不会立即完成,但会在-main返回时完成。

    - (void)main
    {
        // 1- download
        if ([self isCancelled]) return;
    
        // 2- parse
        // 2.1 read file location
    
        // 2.2 load into memory
        while (![self isCancelled] && [self hasNextLineToParse]) {
            // ...
        }
    
        // 3- import
    
        // 3.1 fetch core data request
        if ([self isCancelled]) return;
    
    
        // 3.2 if data is not present, insert it (or update)
        // 3.3 save data into persistent store coordinator
    }
    

    如果您未在-main中检查取消的标志,则该操作不会对取消作出反应,而是一直运行直至完成。

    修改2

    如果操作被取消,除了isCancelled标志设置为true之外没有任何反应。原始答案中的上述代码在后台等待,直到操作完成(或者对取消作出反应或者简单地完成,假设不需要10分钟取消它)。

    当然,在我们的操作中对isCancelled做出反应时,你必须确保将操作保持在未损坏的状态,例如,在下载后直接(只是忽略数据),或者在写入之后所有数据。

    你是对的,如果一个操作被取消但是当你切换回前台时仍然在运行,那个操作将完成下载,然后(如果你这样编程)反应取消并基本上丢弃下载的数据。

    你可以做的不是取消操作,而是等待它们完成(假设它们不到10分钟)。为此,只需删除行[_queue cancelAllOperations];