gpt4 book ai didi

Ios Core Data with 3 MIC 解决方案(应用程序在保存过程中卡住)

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:17:57 25 4
gpt4 key购买 nike

我在我的应用程序中使用 coredata,有 3 个上下文:

  • __masterManagedObjectContext -> 是具有 NSPersistentStoreCoordinator 并将数据保存到磁盘的上下文。

  • _mainManagedObjectContext -> 是应用程序使用的上下文,无处不在

  • dispatchContext -> 在后台方法中使用的上下文,我可以在其中访问我的网络服务和所有核心数据插入/更新内容。

我会放一些代码来实现我的解决方案:

应用初始化代码:

- (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 中调用以访问核心数据数据。

- (NSManagedObjectContext *)managedObjectContext
{
return _mainManagedObjectContext;
}

调用示例:

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

...fetching, update, ...

现在,我真正的问题是:

每当要保存主上下文时([__masterManagedObjectContext save:nil];,在后台),当我尝试访问主上下文(_mainManagedObjectContext)时,应用程序卡住(可能是锁?)。

保存过程需要很长时间(因为有很多数据(大约 6mb))。保存时,应用程序变慢,如果我在此过程运行时访问某些数据,我的应用程序将永远卡住(我需要强制退出)。

另一个问题是合并上下文。想象一下,在其他 viewController 中使用主上下文并保存该上下文,一切正常,直到我关闭应用程序。当我再次打开该应用程序时,没有任何内容被保存。

我做错了什么?直到现在,这种情况让我感到困惑。有人可以帮助我吗?我真的很感激:)

------------编辑:

根据 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];
}

正在恢复...

  • 第 1 - 我创建我的主要上下文
  • 第二 - 我定义后台上下文通知,合并更改保存后。
  • 第三 - 我调用后台方法
  • 4rd - 我保存我的背景上下文

而且性能真的更好。但是应用程序卡住了一点,我认为是在“mergeChanges”上。我做错了什么?

最佳答案

当使用 NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType 时,您应该将在这些上下文中所做的一切都包装在 performBlock: 中。这可以确保这些命令在正确的队列中执行。例如,当您保存主上下文时:

[__masterManagedObjectContext performBlock:^{
[__masterManagedObjectContext save];
}];

此外,如果将调度上下文设置为主上下文的子上下文,则不必手动合并调度上下文中的更改。一旦您保存子上下文,更改就会被推送到父上下文中。

另一个问题是您在一个线程上使用 NSConfinementConcurrencyType 初始化上下文,然后在另一个线程上使用它。对于这种并发类型,在要使用它的线程上初始化上下文非常重要。

但是,我建议您根本不要使用 NSConfinementConcurrencyType。一种可能的替代方法是使用 NSPrivateQueueConcurrencyType 设置您的调度上下文:

NSManagedObjectContext* dispatchContext = [[NSManagedObjectContext] alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
dispatchContext.parentContext = __masterManagedObjectContext;

[dispatchContext performBlock:^{
[self parseDataWithObjects];
}];

执行此操作时无需获取锁,您在 block 内所做的一切都将在私有(private)串行队列中处理。

性能

您已经提到保存需要很长时间并且您的应用变得无响应。

如果您计划在调度上下文中导入大量数据,您的托管对象上下文设置与三个嵌套上下文(private <- main <- dispatch)不是最佳选择。这将始终阻塞主线程很长一段时间,因为您在调度上下文中所做的所有更改都必须先复制到主上下文中,然后才能保存在“根”上下文中。

我最近写了一篇关于这个的文章,comparing the performance of different core data setups .在 follow up post我将更详细地解释为什么此设置在主线程上花费如此多的时间。

对于导入大量数据,使用具有公共(public)持久存储协调器的独立托管对象上下文要快得多。您使用 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];
}];
}

希望这对您有所帮助!

关于Ios Core Data with 3 MIC 解决方案(应用程序在保存过程中卡住),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16666725/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com