如何将正常的核心数据变为后台?

Posted

技术标签:

【中文标题】如何将正常的核心数据变为后台?【英文标题】:How to change normal core data into back ground? 【发布时间】:2015-04-08 06:49:43 【问题描述】:

我的应用中有核心数据,它在主线程中运行。但是现在我正在从服务器接收大量数据并保存到 coredata,现在我的速度变慢了。我在堆栈溢出中搜索了解决方案,发现我需要使用后台核心数据保存。那么它会解决我的问题吗?

如果是,我很困惑如何将现有方法更改为后台核心数据?

这是我现在用来保存到核心数据中的代码。

     -(void)insertToUserEntityWithData:(NSMutableDictionary *)inDictionary

 

   AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication   sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];

UserInfo *userInfo = [NSEntityDescription
                      insertNewObjectForEntityForName:@"UserInfo"
                      inManagedObjectContext:context];

if([[inDictionary allKeys] containsObject:@"userEmail"])

    if([inDictionary valueForKey:@"userEmail"]!=[NSNull null])
    
        userInfo.userEmail=[inDictionary valueForKey:@"userEmail"];
    

if([[inDictionary allKeys] containsObject:@"password"])

    if([inDictionary valueForKey:@"password"]!=[NSNull null])
    
        userInfo.password=[inDictionary valueForKey:@"password"];
    

if([[inDictionary allKeys] containsObject:@"isCurrentUser"])

    if([inDictionary valueForKey:@"isCurrentUser"]!=[NSNull null])
    
        userInfo.isCurrentUser=[NSNumber numberWithBool:[[inDictionary valueForKey:@"isCurrentUser"] boolValue]];
    

if([[inDictionary allKeys] containsObject:@"login_time"])

    if([inDictionary valueForKey:@"login_time"]!=[NSNull null])
    
        userInfo.loginTimestamp=[NSString stringWithFormat:@"%@",[inDictionary valueForKey:@"login_time"]];

    


if([inDictionary valueForKey:@"sid"]!=[NSNull null])

    userInfo.sessionID=[inDictionary valueForKey:@"sid"];


NSError *error;
if (![context save:&error]) 
    NSLog(@"Could not insert to userInfo: %@", [error localizedDescription]);



更新的代码崩溃

 015-04-08 12:59:32.935 Inxed[3401:158275] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Parent NSManagedObjectContext must use either NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType.'
*** First throw call stack:
 (
0   CoreFoundation                      0x035fb946 __exceptionPreprocess + 182
1   libobjc.A.dylib                     0x03284a97 objc_exception_throw + 44
2   CoreData                            0x013b224d -[NSManagedObjectContext setParentContext:] + 269
3   Inxed                               0x002463de __55-[IXDataBaseManager updateThreadEntityWithSyncDetails:]_block_invoke + 318
4   libdispatch.dylib                   0x03fa030a _dispatch_call_block_and_release + 15
5   libdispatch.dylib                   0x03fc0e2f _dispatch_client_callout + 14
6   libdispatch.dylib                   0x03fa6afc _dispatch_queue_drain + 1475
7   libdispatch.dylib                   0x03fa63c3 _dispatch_queue_invoke + 212
8   libdispatch.dylib                   0x03fa9067 _dispatch_root_queue_drain + 466
9   libdispatch.dylib                   0x03faa84a _dispatch_worker_thread3 + 115
10  libsystem_pthread.dylib             0x0431c296 _pthread_wqthread + 724
11  libsystem_pthread.dylib             0x04319eea start_wqthread + 30
 )
libc++abi.dylib: terminating with uncaught exception of type NSException

ma​​nagedObjectContext

  - (NSManagedObjectContext *)managedObjectContext

  managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];


if (_managedObjectContext != nil) 
    return _managedObjectContext;


NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) 
    _managedObjectContext = [[NSManagedObjectContext alloc] init];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];

return _managedObjectContext;

retrieveCurrentUserDetailsForUser

  -(UserInfo *)retrieveCurrentUserDetailsForUser:(NSString*)inUserEmail

  AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"UserInfo"
                                          inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"userEmail == %@",inUserEmail];
[fetchRequest setPredicate:userPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
if(fetchedObjects.count!=0)

    UserInfo *userInfo=[fetchedObjects objectAtIndex:0];
    return userInfo;


return nil;


崩溃日志

   2015-04-08 14:17:29.789 Inxed[3519:170514] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSPersistentStoreCoordinator for searching for entity name 'UserInfo''

 *** First throw call stack:
(
0   CoreFoundation                      0x03673946 __exceptionPreprocess + 182
1   libobjc.A.dylib                     0x032fca97 objc_exception_throw + 44
2   CoreData                            0x013d8ba9 +[NSEntityDescription entityForName:inManagedObjectContext:] + 281
3   Inxed                               0x002cf47c -[IXDataBaseManager retrieveCurrentUserDetailsForUser:] + 316
4   Inxed                               0x001a9268 -[AppDelegate application:didFinishLaunchingWithOptions:] + 2552
5   UIKit                               0x01ccd97c -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 291
6   UIKit                               0x01cce687 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 2869
7   UIKit                               0x01cd1c0d -[UIApplication _runWithMainScene:transitionContext:completion:] + 1639
8   UIKit                               0x01cea7d0 __84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke + 59
9   UIKit                               0x01cd081f -[UIApplication workspaceDidEndTransaction:] + 155
10  FrontBoardServices                  0x056ed9de __37-[FBSWorkspace clientEndTransaction:]_block_invoke_2 + 71
11  FrontBoardServices                  0x056ed46f __40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 54
12  FrontBoardServices                  0x056ff425 __31-[FBSSerialQueue performAsync:]_block_invoke + 26
13  CoreFoundation                      0x035971c0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 16
14  CoreFoundation                      0x0358cad3 __CFRunLoopDoBlocks + 195
15  CoreFoundation                      0x0358c92b __CFRunLoopRun + 2715
16  CoreFoundation                      0x0358bbcb CFRunLoopRunSpecific + 443
17  CoreFoundation                      0x0358b9fb CFRunLoopRunInMode + 123
18  UIKit                               0x01cd01e4 -[UIApplication _run] + 571
19  UIKit                               0x01cd38b6 UIApplicationMain + 1526
20  Inxed                               0x002160ad main + 141
21  libdyld.dylib                       0x04064ac9 start + 1
22  ???                                 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

【问题讨论】:

将这段代码写在后台块中。 但是Server会频繁发送数据,会不会有线程杀或者Partial execution的问题? 检查一下,但我认为这不会造成任何问题。 【参考方案1】:

创建您的后台队列,通过为该块创建 contextFor 将您的插入代码分派到该队列上

不要在后台线程上使用 mainContext

查看修改后的代码。

-(void)insertToUserEntityWithData:(NSMutableDictionary *)inDictionary


    dispatch_queue_t backgroundQueue = dispatch_queue_create("backgroundQueueName", NULL);

    dispatch_async(backgroundQueue, ^
        AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication   sharedApplication] delegate];
        NSManagedObjectContext *context = [sharedDelegate managedObjectContext];

        NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

        contextforThread.parentContext = context;

        UserInfo *userInfo = [NSEntityDescription
                              insertNewObjectForEntityForName:@"UserInfo"
                              inManagedObjectContext:contextforThread];

        if([[inDictionary allKeys] containsObject:@"userEmail"])
        
            if([inDictionary valueForKey:@"userEmail"]!=[NSNull null])
            
                userInfo.userEmail=[inDictionary valueForKey:@"userEmail"];
            
        
        if([[inDictionary allKeys] containsObject:@"password"])
        
            if([inDictionary valueForKey:@"password"]!=[NSNull null])
            
                userInfo.password=[inDictionary valueForKey:@"password"];
            
        
        if([[inDictionary allKeys] containsObject:@"isCurrentUser"])
        
            if([inDictionary valueForKey:@"isCurrentUser"]!=[NSNull null])
            
                userInfo.isCurrentUser=[NSNumber numberWithBool:[[inDictionary valueForKey:@"isCurrentUser"] boolValue]];
            
        
        if([[inDictionary allKeys] containsObject:@"login_time"])
        
            if([inDictionary valueForKey:@"login_time"]!=[NSNull null])
            
                userInfo.loginTimestamp=[NSString stringWithFormat:@"%@",[inDictionary valueForKey:@"login_time"]];

            
        

        if([inDictionary valueForKey:@"sid"]!=[NSNull null])
        
            userInfo.sessionID=[inDictionary valueForKey:@"sid"];
        

        if (![contextforThread save:&error]) 
            NSLog(@"Could not insert to userInfo: %@", [error localizedDescription]);
        

        dispatch_async(dispatch_get_main_queue(), ^
            NSError *error;
            if (![context save:&error]) 
                NSLog(@"Could not insert to userInfo: %@", [error localizedDescription]);
            
            else
            
                //Post a notification or call method for tableview reload here
            

        );


    );



编辑

在您的 AppDelegate.m 中找到您的方法 ma​​nagedObjectContext

并进行以下更改

managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

编辑 2

查看我的核心数据方法。做出相应的改变。

请参阅:您的 NSManagedObjectContext 只能在您的 - (NSManagedObjectContext *) managedObjectContext 方法中创建

- (NSManagedObjectContext *) managedObjectContext 
    if (managedObjectContext != nil) 
        return managedObjectContext;
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) 
        managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    

    //[managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    return managedObjectContext;


- (NSManagedObjectModel *)managedObjectModel 
    if (managedObjectModel != nil) 
        return managedObjectModel;
    
    managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];

    return managedObjectModel;


- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
    if (persistentStoreCoordinator != nil) 
        NSLog(@"old persistent returned!!!");
        return persistentStoreCoordinator;
    
    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
                                               stringByAppendingPathComponent: @"<Project Name>.sqlite"]];
    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
                                  initWithManagedObjectModel:[self managedObjectModel]];
    if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                 configuration:nil URL:storeUrl options:nil error:&error]) 
        /*Error for store creation should be handled in here*/
    
    NSLog(@"new persistent created!!!");
    return persistentStoreCoordinator;


- (NSString *)applicationDocumentsDirectory 
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

【讨论】:

评论不用于扩展讨论;这个对话是moved to chat。【参考方案2】:

从您的后台线程创建一个私有上下文并将其父上下文设置为主上下文。

NSManagedObjectContext* childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = self.mainContext;

在后台线程中使用此子上下文插入/更新/删除您的托管对象,然后将子上下文与主上下文一起保存,如下所示

[childContext performBlock:^
   // do something that takes some time asynchronously using the temp context

  // push to parent
  NSError *error;
  if (![childContext save:&amp;error])
  
    // handle error
  

  // save parent to disk asynchronously
 [self.mainContext performBlock:^
    NSError *error;
    if (![self.mainContext save:&amp;error])
    
      // handle error
   
 ];
];

【讨论】:

我已经尝试过了,但是崩溃了。你能把代码更详细一点吗?

以上是关于如何将正常的核心数据变为后台?的主要内容,如果未能解决你的问题,请参考以下文章

iPhone 编程 - 使用核心数据进行后台保存

Swift:在后台同步核心数据对象

核心数据 - 在后台保存到磁盘上的持久存储

在位置后台模式下从核心数据访问数据

后台返回的中文数据是乱码,如何解析成正常字符

如何让Outlook随系统启动+关闭变为后台运行