4

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

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

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

当应用程序进入后台时,我需要停止(或继续)操作。从这些讨论(AFNetworking 有后台支持吗?NSOperations 队列和处理应用程序退出)我看到最好的方法是取消操作并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在每一步之前检查财产吗?这行得通吗?

编辑 2 基于 Tammo Freese 的编辑

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

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

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

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

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

4

1 回答 1

15

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

  1. 取消所有操作并
  2. 等到取消处理完毕。

通常这不应该花费太多时间,所以这样做就足够了:

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

此处“过多时间”的定义约为 5 秒:如果您阻塞-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];

于 2012-11-12T15:21:48.087 回答