核心数据和多线程在保存时崩溃
Posted
技术标签:
【中文标题】核心数据和多线程在保存时崩溃【英文标题】:Core Data and Multithreading crashes on save 【发布时间】:2012-05-08 13:12:43 【问题描述】:我正在构建一个应用程序,该应用程序从远程服务器提取数据并将它们存储在 CoreData SQLite 数据库中。我正在从后台线程获取数据,而主线程正在使用它。以下是我使用的主要方法。
-
所有后台线程都有自己的
NSManagedObjectContext
。
persistentStoreCoordinator
在上下文之间共享。
在后台线程的每次提取中,我都会保存:插入的对象并重置:本地上下文。
我使用通知与主线程上下文进行合并。
我遇到的问题:
随机我在后台线程保存时遇到没有消息的崩溃(NSZombie
和崩溃断点已设置):操作。
正在生成大量重复数据。网络服务数据肯定没问题,所以服务器端没有问题。
这里是代码。
AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
// Update data from remote server
WebserviceDataModel *webservice = [[WebserviceDataModel alloc] init];
webservice.managedObjectContext = self.managedObjectContext;
[webservice startImport];
后台线程在 WebserviceDataModel 上获取和保存数据
- (void)startImport
dispatch_queue_t downloadQueue = dispatch_queue_create("startImport in WebserviceDataModel", NULL);
dispatch_async(downloadQueue, ^
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
moc.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;
// get the remote data
NSDictionary *lojas = [Loja allLojasFromRemoteServer];
for (NSDictionary *lojaInfo in lojas)
Loja *loja __attribute__((unused)) = [Loja lojaWithRemoteData:lojaInfo inManagedObjectContext:moc];
if ([moc hasChanges])
[moc save:nil];
[moc reset];
[moc release];
);
dispatch_release(downloadQueue);
用于创建对象的 NSManagedObject 方法:+ (Loja *)lojaWithRemoteData:inManagedContext:
+ (Loja *)lojaWithRemoteData:(NSDictionary *)remoteData inManagedObjectContext:(NSManagedObjectContext *)context
Loja *loja = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"Loja" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:@"lojaId = %d", [[remoteData objectForKey:@"lojaId"] intValue]];
NSError *error = nil;
loja = [[context executeFetchRequest:request error:&error] lastObject];
[request release];
if (!error && !loja)
// create the record
loja = [NSEntityDescription insertNewObjectForEntityForName:@"Loja" inManagedObjectContext:context];
loja.lojaId = [remoteData objectForKey:@"lojaId"];
loja.email = [remoteData objectForKey:@"email"];
loja.facebook = [remoteData objectForKey:@"facebook"];
// ... and others...
return loja;
在 WebserviceDataModel 上订阅 NSManagedObjectContextDidSaveNotification
- (id)init
self = [super init];
if (self)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
return self;
WebserviceDataModel 上的 contextChanged: 方法
- (void)contextChanged:(NSNotification*)notification
if ([notification object] == self.managedObjectContext) return;
if (![NSThread isMainThread])
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
【问题讨论】:
好吧,我想通了。它现在就像一个魅力。多线程上下文的实现是正确的。问题出在applicationDidBecomeActive:
我的应用程序使用CoreLocation Fence 来获取功能列表。当应用程序第一次启动时,CoreLocation 框架会向用户显示一条警报消息,指出该应用程序使用用户位置...该警报再次调用applicationDidBecomeActive:
,创建两个并发更新波。刚刚将我的 WebserviceDataModel 移动到一个属性并实现了一个标志来知道它是否正在运行。就是这样。
【参考方案1】:
嗯,我想通了。它现在就像一个魅力。多线程上下文的实现是正确的。问题在于applicationDidBecomeActive:
我的应用程序使用CoreLocation Fence 来获取功能列表。当应用程序首次启动时,CoreLocation 框架会向用户显示一条警报消息,指出该应用程序使用用户位置...该警报再次调用applicationDidBecomeActive:
,创建两个并发更新波。只需将我的 WebserviceDataModel 移动到一个属性并实现一个标志以了解它是否正在运行。就是这样
只是为了最后的改进,将合并策略更改为NSMergeByPropertyStoreTrumpMergePolicy
,所以现在服务器端数据(在内存中)胜过本地存储。
【讨论】:
【参考方案2】:没有细节就很难理解发生了什么。
说了这么多,首先尝试设置断点,按照应用程序流程。也许你能找到你的应用崩溃的地方。
那么,你确定lojaId
是一个标量值吗?当您创建一个新实体时,您编写了以下内容:
loja.lojaId = [remoteData objectForKey:@"lojaId"];
也许这可能是错误,所以我将尝试以下谓词:
[NSPredicate predicateWithFormat:@"lojaId == %@", [remoteData objectForKey:@"lojaId"]];
最后,当您进行保存时,请尝试记录 NSError
对象。也许您可以找到崩溃的原因。
if ([moc hasChanges])
NSError* error = nil;
[moc save:&error];
if(error)
NSLog(@"Error during save: %@\n%@", [error localizedDescription], [error userInfo]);
abort(); // only for debug purposes
[moc reset];
希望对你有帮助。
【讨论】:
loja.lojaId 是一个 NSNumber,所以这就是使用该格式挂载谓词的方式。我已经弄清楚了问题所在。检查问题上的 cmets。谢谢!! @GeorgeVillasboas 好的,没问题。发布您的解决方案作为答案。谢谢。以上是关于核心数据和多线程在保存时崩溃的主要内容,如果未能解决你的问题,请参考以下文章