Основные данные. Получают ли дочерние контексты постоянные объектные идентификаторы для вновь вставленных объектов?
У меня есть приложение с двумя настройками контекста управляемого объекта:
- Контекст родителя: NSPrivateQueueConcurrencyType, связанный с постоянным хранилищем.
- Основной контекст: NSMainQueueConcurrencyType, дочерний элемент родительского контекста.
При вставке нового управляемого объекта в основной контекст я сохраняю основной контекст, а затем родительский контекст, например:
[context performBlockAndWait:^{
NSError * error = nil;
if (![context save: &error]) {
NSLog(@"Core Data save error %@, %@", error, [error userInfo]);
}
}];
[parentContext performBlock:^{
NSError *error = nil;
BOOL result = [parentContext save: &error];
if ( ! result ) {
NSLog( @"Core Data save error in parent context %@, %@", error, [error userInfo] );
}
}];
Я понимаю, что когда объект управления сначала создается, он имеет временный objectID
. Затем сохраняется основной контекст, и этот объект с его временным идентификатором переходит в родительский контекст. Затем сохраняется родительский контекст. Когда этот последний контекст сохраняется, временный objectID
в родительском контексте преобразуется в постоянный objectID
.
Итак:
- Означает ли постоянный идентификатор объекта автоматическое возвращение
к основному (дочернему) контексту?
- Когда я принудительно получаю постоянный идентификатор объекта с помощью
[NSManagedObjectContext obtainPermanentIDsForObjects:error:]
, затем запустите приложение, активируйте его, перезагрузите, получите объект с помощью основного контекста objectWithID:
и получите доступ к свойству, я получаю
"CoreData не может выполнить ошибку для...".
Что не так с этим подходом?
Ответы
Ответ 1
Это известная ошибка, надеюсь, исправлена в ближайшее время, но в целом получение постоянного идентификатора достаточно, если вы делаете это до сохранения данных в первом дочернем элементе, и вы включаете только вставленные объекты:
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:&error]
В некоторых сложных случаях лучше получить постоянный идентификатор сразу после создания экземпляра, особенно если у вас сложные отношения.
Как и когда вы звоните obtainPermanentIDsForObjects
?
Я не следую за частью, связанной с отключением приложения. Возможно, лучшее объяснение поможет.
Ответ 2
Как сказал Джоди выше, при создании нового NSManagedObject в фоновом потоке с использованием дочернего ManagedObjectContext вы должны принудительно создать постоянный идентификатор, выполнив следующее до того, как вы сохраните:
NSError *error = nil;
[threadedMOC obtainPermanentIDsForObjects:threadedMOC.insertedObjects.allObjects error:&error];
BOOL success = [threadedMOC save:&error];
ИМХО, это не очень интуитивно, чтобы сделать это таким образом - ведь вы просите постоянный идентификатор до того, как вы сэкономите! Но так оно и работает. Если вы запрашиваете постоянный идентификатор после сохранения, то идентификатор будет по-прежнему временным. В Документах Apple вы можете использовать следующее, чтобы определить, является ли ID объекта временным:
BOOL isTemporary = [[managedObject objectID] isTemporaryID];
Ответ 3
Проблема все еще существует в iOS 8.3 Решение в Swift:
func saveContext(context: NSManagedObjectContext?){
NSOperationQueue.mainQueue().addOperationWithBlock(){
if let moc = context {
var error : NSError? = nil
if !moc.obtainPermanentIDsForObjects(Array(moc.insertedObjects), error: &error){
println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)")
}
if moc.hasChanges && !moc.save(&error){
println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)")
}
}
}
}
func saveBackgroundContext(){
saveContext(self.defaultContext)
privateContext?.performBlock{
var error : NSError? = nil
if let context = self.privateContext {
if context.hasChanges && !context.save(&error){
println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)")
}else {
println("saved private context to disk")
}
}
}
}
Где:
- defaultContext имеет concurrencyType.MainQueueConcurrencyType
- privateContext имеет concurrencyType.PrivateQueueConcurrencyType