如何将正常的核心数据变为后台?
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
managedObjectContext
- (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 中找到您的方法 managedObjectContext
并进行以下更改
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:&error])
// handle error
// save parent to disk asynchronously
[self.mainContext performBlock:^
NSError *error;
if (![self.mainContext save:&error])
// handle error
];
];
【讨论】:
我已经尝试过了,但是崩溃了。你能把代码更详细一点吗?以上是关于如何将正常的核心数据变为后台?的主要内容,如果未能解决你的问题,请参考以下文章