【发布时间】:2013-05-21 09:34:55
【问题描述】:
我在我的应用中使用 coredata,有 3 个上下文:
__masterManagedObjectContext -> 是具有 NSPersistentStoreCoordinator 并将数据保存到磁盘的上下文。
_mainManagedObjectContext -> 是应用使用的上下文,无处不在
dispatchContext -> 后台方法中使用的上下文,我可以在其中访问我的 Web 服务和所有核心数据插入/更新内容。
我会放一些代码来实现我的解决方案:
应用初始化代码:
- (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)时,应用程序就会冻结(可能是锁?)。
保存过程需要很长时间(因为数据量很大(大约 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];
}
正在恢复中...
- 第一个 - 我创建我的主要上下文
- 2nd - 我定义了后台上下文通知,以合并更改 保存后。
- 3rd - 我调用后台方法
- 4rd - 我保存了我的背景上下文
而且性能确实更好。但是该应用程序冻结了一点,我认为是在“mergeChanges”上。我做错了什么?
【问题讨论】:
-
你不需要跳
[NSThread detachNew...]舞蹈。您可以使用NSPrivateQueueConcurrencyType初始化背景上下文,然后执行 `[backgroundContext performBlock:^{ ...your importing code... }]。您在此块中所做的一切都将在后台线程上执行。为了减少合并的影响,您应该定期保存后台 MOC。不要导入所有内容然后保存,而是每 100-200 个对象保存一次。 -
Soo,我所有的后台代码都需要在 [backgroundContext performBlock:^{ ...您的导入代码... }] 内?我需要在 NSPrivateQueueConcurrencyType 中调用我的后台方法?
-
是的,它需要在里面,但当然不是字面意思,你可以从块中调用一个方法。块本身将在后台执行,因此在这种情况下,您只需在后台上下文中调用
performBlock:即可获得并发。 -
如果我理解的话,每 100/200 个对象,我都会在背景上下文中“保存”一次。每次保存时,是否与主上下文合并?这是个好主意吗?
-
我已经尝试保存每 200 个对象,所以我遇到了同样的问题.. 用户界面变慢,有一些滞后:/
标签: ios objective-c core-data