我在我的应用中使用coredata,有3个上下文:
__ masterManagedObjectContext - > 是具有NSPersistentStoreCoordinator并将数据保存到磁盘的上下文。
_mainManagedObjectContext - > 是app在任何地方使用的上下文
dispatchContext - > 在后台方法中使用的上下文,我有我的webservice访问权限和所有coredata插入/更新内容。
我会用一些代码来实现我的解决方案:
应用初始化代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions //a app começa aqui
{
NSPersistentStoreCoordinator *coordinator = [self newPersistentStoreCoordinator];
__masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[__masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainManagedObjectContext setUndoManager:nil];
[_mainManagedObjectContext setParentContext:__masterManagedObjectContext];
return YES;
}
创建新店铺协调员的方法
- (NSPersistentStoreCoordinator *)newPersistentStoreCoordinator
{
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"example.sqlite"];
NSError *error = nil;
NSPersistentStoreCoordinator *pC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self newManagedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![pC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return pC;
}
- (NSManagedObjectModel *)newManagedObjectModel
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"example" withExtension:@"momd"];
NSManagedObjectModel *newManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return newManagedObjectModel;
}
带上下文规范的线程调用(基本代码):
@try
{
dispatchContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[dispatchContext setUndoManager:nil];
[dispatchContext setParentContext:__masterManagedObjectContext];
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:dispatchContext];
if(dispatchContext != nil)
{
[NSThread detachNewThreadSelector:@selector(parseDataWithObjects) toTarget:self withObject:nil];
}
else
{
NSLog(@"context IS NIL");
}
}
背景方法:
- (void)parseDataWithObjects
{
[dispatchContext lock];
...
webservice data parse, and core data inserting/updating (+/- 5MB)
...
[dispatchContext save:&error];
[dispatchContext unlock];
[__masterManagedObjectContext save:nil];
}
此方法在所有UI中都可以访问coredata数据。
- (NSManagedObjectContext *)managedObjectContext
{
return _mainManagedObjectContext;
}
一个调用示例:
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
...fetching, update, ...
现在,我真正的问题是:
每当要保存主要上下文([__masterManagedObjectContext save:nil];
,在后台)时,当我尝试访问主要上下文(_mainManagedObjectContext
)时,应用程序会冻结(可能是锁定?)。
保存过程需要很长时间(因为有大量数据(aprox.6mb))。保存时,应用程序变慢,如果我在此过程运行时访问某些数据,我的应用程序将永久冻结(我需要强制退出)。
另一个问题是合并上下文。想象一下,使用其他viewController中的主要上下文,并保存该上下文,一切正常,直到我关闭de app。当我再次打开应用程序时,没有保存任何内容。
我做错了什么?直到现在,这种情况让我感到困惑。 有人可以帮帮我吗?我真的很感激:))
---------- 编辑:
按照Florian Kugler的回应,现在我只有2个上下文,每个都有相同的协调员。
当我的应用初始化时,我称之为
-(void) createContexts
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"example" withExtension:@"momd"];
NSManagedObjectModel *newManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"example.sqlite"];
NSError *error = nil;
pC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:newManagedObjectModel];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![pC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainManagedObjectContext.persistentStoreCoordinator = pC;
}
- (void)mergeChanges:(NSNotification*)notification {
[mainManagedObjectContext performBlock:^{
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
- (void)saveMasterContext
{
[mainManagedObjectContext performBlock:^{
[mainManagedObjectContext save:nil];
}];
}
要开始我的数据导入(在后台),我使用以下代码:
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:backgroundContext];
[NSThread detachNewThreadSelector:@selector(parseDataWithObjects) toTarget:self withObject:nil];
我的背景方法:
- (void)parseDataWithObjects
{
[self resetTime];
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.persistentStoreCoordinator = pC;
...
[backgroundContext save:&error];
}
...恢复
表现真的好多了。但该应用冻结了一点,我认为是“mergeChanges”。我做错了什么?
答案 0 :(得分:6)
使用NSPrivateQueueConcurrencyType
或NSMainQueueConcurrencyType
时,您应该在performBlock:
中包装您在这些上下文中执行的所有操作。这可以确保这些命令在正确的队列中执行。例如,当您保存主上下文时:
[__masterManagedObjectContext performBlock:^{
[__masterManagedObjectContext save];
}];
此外,如果将其设置为主上下文的子项,则不必手动从调度上下文合并更改。保存子上下文后,更改将被推送到父上下文中。
另一个问题是您在一个线程上使用NSConfinementConcurrencyType初始化上下文,然后在另一个线程上使用它。使用此并发类型,在您要使用它的线程上初始化上下文非常重要。
但是,我建议你根本不要使用NSConfinementConcurrencyType。一种可能的替代方法是使用NSPrivateQueueConcurrencyType
设置您的调度上下文:
NSManagedObjectContext* dispatchContext = [[NSManagedObjectContext] alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
dispatchContext.parentContext = __masterManagedObjectContext;
[dispatchContext performBlock:^{
[self parseDataWithObjects];
}];
执行此操作时,无需获取锁定,您在块中执行的所有操作都将在专用串行队列中处理。
您已经提到保存需要很长时间,并且您的应用无法响应。
如果您计划在调度上下文中导入大量数据,则具有三个嵌套上下文(private< - main< - dispatch)的托管对象上下文设置不是最佳选择。这将始终阻塞主线程大量时间,因为您在调度上下文中所做的所有更改必须先复制到主上下文中,然后才能将它们保存在“根”上下文中。
我最近写了一篇关于此的文章comparing the performance of different core data setups。在follow up post中,我更详细地解释了为什么这个设置在主线程上需要花费很多时间。
对于导入大量数据,使用具有公共持久性存储协调器的独立托管对象上下文要快得多。您可以使用NSMainQueueConcurrencyType
(用于所有与UI相关的内容)创建一个上下文,使用NSPrivateQueueConcurrencyType
创建另一个上下文(用于导入数据)。您将相同的持久性存储协调器分配给它们。
// assuming you have persistendStoreCoordinator
NSManagedObjectContext* mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainContext.persistentStoreCoordinator = persistentStoreCoordinator;
NSManagedObjectContext* backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.persistentStoreCoordinator = persistentStoreCoordinator;
此设置不提供嵌套上下文所带来的自动更改传播,但通过保存通知很容易实现。请注意再次使用performBlock:
,以便合并发生在正确的线程上:
// ...
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:backgroundContext];
// ...
- (void)mergeChanges:(NSNotification*)notification {
[mainContext performBlock:^{
[mainContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
希望这有帮助!