将应用程序推送到后台时,NSManagedObjectContext 不会获取或保存对象

Posted

技术标签:

【中文标题】将应用程序推送到后台时,NSManagedObjectContext 不会获取或保存对象【英文标题】:NSManagedObjectContext doesnt fetch or save object when application is pushed to background 【发布时间】:2014-07-08 12:48:13 【问题描述】:

在我的应用程序中,我有一个父子上下文。将对象保存到处理自身的核心数据是在 dispatch_async 中完成的。在前台一切正常,但是当应用程序被推送到后台时,获取请求和保存操作被暂停。我也在使用 NSOperationQueue 来排队我的操作。虽然在所有操作都可以正常工作的前台内存使用情况稳定,但当被推送到后台时,即使内存消耗量也会大幅增加,并且应用程序在内存压力下崩溃。

- (void) saveDocument:(ALEXDocument*)document forDownloaded:(BOOL)isDownloadCompleted


    BOOL checkWithKillDocId = NO;
    if(!document.tocId || [document.tocId isEqualToString:@""])
        document.tocId = nil;

    NSManagedObjectContext *context = [self getNewPrivateManagedObjectContext];

    NSFetchRequest *request         = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity     = [NSEntityDescription entityForName:@"Document" inManagedObjectContext:context];
    [request setEntity:entity];

    NSPredicate *predicate;

    if (![document.killDocId isEqual:[NSNull null]] && document.killDocId!= nil)
    
        if(![document.killDocId isEqualToString:@"(null)"] && ![document.killDocId isEqualToString:@""])
        
            predicate = [NSPredicate predicateWithFormat:@"killDocId = %@",document.killDocId];
            checkWithKillDocId = YES;
        
        else
        
            predicate                       = [ NSPredicate predicateWithFormat:@"reference == %@ AND section = %d AND tocRelation.tocId = %@ ", document.reference,document.section,document.tocId];
            checkWithKillDocId = NO;
        

    
    else
    
         predicate                       = [ NSPredicate predicateWithFormat:@"reference == %@ AND section = %d AND tocRelation.tocId = %@ ", document.reference,document.section,document.tocId];
        checkWithKillDocId = NO;
    

    [request setPredicate:predicate];

    NSError *error;

    Document *coreDataDoc          = (Document*)[[context executeFetchRequest:request error:&error] lastObject];
    NSLog(@"Document");
    if (!coreDataDoc || coreDataDoc == nil)
    
        if(checkWithKillDocId)
        
            predicate                       = [ NSPredicate predicateWithFormat:@"reference == %@ AND section = %d AND tocRelation.tocId = %@ ", document.reference,document.section,document.tocId];
            [request setPredicate:predicate];
            coreDataDoc          = (Document*)[[context executeFetchRequest:request error:&error] lastObject];
            if (!coreDataDoc || coreDataDoc == nil)
            
                coreDataDoc = [NSEntityDescription insertNewObjectForEntityForName:@"Document" inManagedObjectContext:context];
            
        
        else
        
             coreDataDoc = [NSEntityDescription insertNewObjectForEntityForName:@"Document" inManagedObjectContext:context];
        

    

    coreDataDoc.anchorName          = document.anchorName;
    coreDataDoc.binary              = document.binary;
    coreDataDoc.contentSize         = [NSNumber numberWithInt:document.contentSize];
    coreDataDoc.copyright           = document.copyright;
    coreDataDoc.createdDate         = document.date;
    coreDataDoc.domain              = document.domain;
    coreDataDoc.fileName            = document.fileName;
    coreDataDoc.htmlContent         = document.htmlContent;
    coreDataDoc.mimeType            = document.mimeType;
    coreDataDoc.reference           = document.reference;
    coreDataDoc.section             = [NSNumber numberWithInteger:document.section];
    coreDataDoc.source              = document.source;
    coreDataDoc.title               = document.title;

    coreDataDoc.tocReference        = document.tocReference;
    coreDataDoc.nextReference       = document.nextReference;
    coreDataDoc.nextSection         = [NSString stringWithFormat:@"%@",document.nextSection];
    coreDataDoc.previousReference   = document.previousReference;
    coreDataDoc.previousSection     = [NSString stringWithFormat:@"%@",document.previousSection];
    coreDataDoc.downloadedDate      = [NSDate date];
    coreDataDoc.status              = document.documentStatus;
    coreDataDoc.killDocId           = document.killDocId;
    [self dbSaveInContext:context];
//    context = nil;
//    coreDataDoc = nil;
//    [context reset];
//    [context refreshObject:coreDataDoc mergeChanges:NO];
    NSLog(@"Context");


【问题讨论】:

【参考方案1】:

要在后台运行任何进程,您需要告诉 ios 您的应用将继续并完成任务。

UIBackgroundTaskIdentifier backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
    NSLog(@"Not running background tasks anymore.");
    [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
    backgroundTask = UIBackgroundTaskInvalid;
];

它可以让您在短时间内在后台运行任何任务。 (10 分钟到 5 秒)。

【讨论】:

感谢您的回答,但我面临的问题是上面定义的 NSManasgedObjectContext 在后台停止所有执行,而在前台正常工作 你在检查后台任务时间吗?可能会少于完成任务所需的时间。 [UIApplication sharedApplication].backgroundTimeRemaining.

以上是关于将应用程序推送到后台时,NSManagedObjectContext 不会获取或保存对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 中运行后台任务,完成后,我可以将信息推送到前端。

Core Data 后台处理,保存不推送到主上下文

推送到 didReceiveRemoteNotification 上的视图

将 CoreData 更改推送到 Web 服务的最佳实践?

真实设备如何将位置信息推送到API?

KIF 无法强制应用程序进入后台