我使用以下代码从Web服务加载数据并将其存储在Core Data DB中。
dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{
NSArray *list = [WebService loadData];
dispatch_sync(dispatch_get_main_queue(), ^{
if (list != nil) {
for (NSDictionary *data in list) {
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *data = [appDelegate.coreDataDatabase.managedObjectContext executeFetchRequest:request error:&error];
Object *object = [data objectAtIndex: 0];
if (object == nil) {
object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext];
object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]];
}
object.name = [data objectForKey:@"name"];
}
[appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
}
});
});
dispatch_release(fetchQ);
代码已简化,但它应代表序列。对Web服务的访问在后台运行。 之后,在主线程中调用存储。应该没问题吧?
不幸的是,整个工作并不适用于所有设备。在我的所有设备和大多数其他设备上,没有问题。 在一些但显然是这样,因为不幸的是在商店里有一些负面的Bewertugnen和几个问题。 问题是他发现数据库中已有的记录而不是新的投资
感谢您的帮助, 斯蒂芬
答案 0 :(得分:1)
我很难理解你的实际问题。我不太确定你的意思......
在一些但显然是如此,因为不幸的是有一些 在商店里负面的Bewertugnen和几个问题。该 问题是他在数据库中找到了已存在的记录,而不是 不断进行新的投资
所以我会尽我所能。
从代码中,我假设您使用的是UIManagedDocument。
您不应直接保存UIManagedDocument
(saveToUrl)。相反,你应该让它处理自动保护,因为它会适当地执行。
如果NSUndoManager
上有UIManagedDocument
有效,则无需执行任何操作。所有操作都将自动保存。如果您没有撤消管理器,则需要戳它以使其知道它有脏数据。你这样做:
[managedDocument updateChangeCount:UIDocumentChangeDone];
但是,请注意UIManagedDocument
使用嵌套上下文,并且存在一些与插入新对象相关的错误。即,持久性ID未被正确地推回到子上下文。我对您的问题的假设是您因此而获得重复的数据条目。
这个SO问题在某种程度上解决了这个问题。
Core Data could not fullfil fault for object after obtainPermanantIDs
基本上,考虑到您希望继续使用UIManagedDocument
,您有多种选择。首先,获取永久ID。替换此行:
[appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
有了这个(当然 - 处理错误 - 这只是传递0):
NSManagedObjectContext *moc = appDelegate.coreDataDatabase.managedObjectContext;
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0];
[appDelegate.coreDataDatabase updateChangeCount:UIDocumentChangeDone];
这仍有一些问题,但它们很少见。您的代码似乎没有非常复杂的对象图,因此这应该足够了。
另一种选择是导入单独的MOC(UIManagedDocument
父上下文的子项),并在导入完成后重新获取主上下文。
使用您的代码示例,一种简单的方法就是......
dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{
NSArray *list = [WebService loadData];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
moc.parentContext = appDelegate.coreDataDatabase.managedObjectContext.parentContext;
for (NSDictionary *data in list) {
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *data = [moc executeFetchRequest:request error:&error];
Object *object = [data objectAtIndex: 0];
if (object == nil) {
object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:moc];
object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]];
}
object.name = [data objectForKey:@"name"];
}
// Save the MOC you just loaded everything into
[moc save:&error]; // handle error...
// Save the parent MOC, which is also the parent of your main MOC
moc = moc.parentContext;
[moc performBlock:^{
[moc save:&error]; // handle error
// When all the saves are done, run on the main queue and just refetch
// all the entities that were inserted.
dispatch_async(dispatch_get_main_queue(), ^{
// Here, use appDelegate.coreDataDatabase.managedObjectContext
// to refetch the objects that were altered. If you are using a
// table view or FRC just issue a refetch of the data and the
// main MOC will get everything that was modified.
});
}];
});
dispatch_release(fetchQ);
答案 1 :(得分:0)
如果你在主线程上创建了你的上下文,那么对它的每次访问也必须在mainthread上(假设pre-ios 5配置 - Core Data支持iOS 5后的3种模式)。
您可以创建一个串行调度队列,在那里创建moc,并通过该队列汇集对它的所有访问。调度队列将访问序列化,因此您永远不会有并发。
我自己刚刚转换为使用iOS5中的私有队列功能,在这种情况下,每次访问都是通过'[moc perform block ...]'完成的 - 转换花了我不到1小时,代码实际得到了clean(我之前一直在使用串行队列)。