在iOS 7中使用NSOperation进行后台获取时,如何调用completionHandler?

9

我希望在我的iOS应用程序中集成后台获取功能,我已经在项目的功能列表中启用了后台获取功能,然后我插入了以下内容:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

要启用最小间隔的后台获取功能,然后在应用程序委托中插入委托方法:

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@"Call fetch iCloud");
    [[MySingleton sharedManager] startCheckOnCloud];
}

我的问题是这样的,我知道我需要调用这个函数:
completionHandler(UIBackgroundFetchResultNewData);

我曾经看到很多关于在performFetchWithCompletionHandler代理方法中插入completionHandler的例子,但是在这个方法中,我调用了一个Singleton中的NSOperation,该操作检查iCloud文件夹并对Sqlite DB进行一些更改。如果我这样做:

    -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
        NSLog(@"Call fetch iCloud");
        [[MySingleton sharedManager] startCheckOnCloud];
        completionHandler(UIBackgroundFetchResultNewData);
    }

操作开始后就没有执行任何操作,可能是因为系统立即将其休眠,如果我删除completionHandler,会收到以下警告:

Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called.

我的问题是,我如何使用NSOperation处理completionHandler

4个回答

7

你必须在30秒内调用完成处理程序,但是在执行查询时不需要阻塞主线程。

似乎UIKit内部检测到您没有存储对完成处理程序的强引用,然后记录该警告。 如果您执行以下简单操作:

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void  (^)(UIBackgroundFetchResult))completionHandler
{
    // Start asynchronous NSOperation, or some other check

    // Ideally the NSOperation would notify when it has completed, but just for
    // illustrative purposes, call the completion block after 20 seconds.
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC), 
                   dispatch_get_main_queue(), ^{
        // Check result of your operation and call completion block with the result
        completionHandler(UIBackgroundFetchResultNewData);
    });
}

你会注意到,即使在调用完成块之前方法已经退出,警告也没有被记录。

伙计,这个的 Swift 版本是什么? - Jayprakash Dubey
@JayprakashDubey func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20 * Int64(NSEC_PER_SEC)), dispatch_get_main_queue()) { completionHandler(UIBackgroundFetchResult.NewData) } } - Logan
你提到强引用跟踪真是太棒了。解决了我的问题! - eonil

6
我在我的应用程序中也遇到了类似的情况。我所做的是将完成处理程序传递给我的单例,无论我的代码路径在操作中结束时,无论是成功还是错误,我都会检查是否有对完成块的引用。如果有,我只需使用适当的结果调用完成处理程序,如果没有,那就意味着我不处于后台获取模式。 不要忘记在完成后将单例中完成处理程序的引用设置为nil,因为它可能会欺骗您的实例进行后续操作。 我还做了其他一些事情,由于后台获取的强制30秒超时,在启动操作之前,我检查是否处于后台获取模式,如果是,则为25秒(5秒保证阈值)安排一个计时器并自行处理该情况。如果您不这样做,并且您的应用程序在调用完成处理程序的30秒超时时间内失败了太多次,您将无法在用户设备上进行后台获取。 祝你好运

1

在某个时刻,你的应用程序将知道何时完成获取和处理新数据。这可能是每当新文件存储到磁盘中、在Core Data中插入新记录或网页加载完成时。

一旦发生这种情况,你必须确定是否确实有新数据,然后使用正确的参数调用完成处理程序。很可能,这意味着你需要将完成处理程序传递给其他对象。startCheckOnCloud将变成startCheckOnCloudWithCompletionHandler:,如果该方法并不是实际执行获取操作的方法,则将完成处理程序传递给在startCheckOnCloudWithCompletionHandler:中被调用的执行获取操作的方法。


0

我也遇到了这个问题,最终解决方法是因为在fetch回调中存在异步操作,当您开始使用__block UIBackgroundFetchResult分配时:

UIBackgroundFetchResult __block result = UIBackgroundFetchResultNoData;
result = UIBackgroundFetchResultNoData; 
post: return; return; because, "did not lead to finally, the method is called," completionHandler (result); "

看起来出现了一个错误:“完成处理程序从未被调用”,“return;”去掉最后的调用方法:“completionHandler(result);”就没有错误了。在总结下面,必须调用方法:completionHandler();不能错。

谢谢。


1
非常感谢您纠正错误~。@JaggenSWE - DaoYu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接