Как правильно инициализировать, использовать и сохранить UIManagedDocument
Я имею дело, чтобы заставить мое приложение работать, используя UIManagedDocument. Я отправил еще один вопрос несколько дней назад, но пока не получил его. Посмотрим, смогу ли я сделать это лучше.
Мое приложение должно загрузить некоторые данные из файла plist и заполнить им базу данных. Затем, есть некоторые параметры, которые будут меняться время от времени, и мне также нужно загрузить и обновить их в базе данных.
Приложение должно следовать следующей последовательности: создать / открыть базу данных, загрузить данные, обновить переменные данные.
Моя проблема возникает, когда я пытаюсь следовать этой последовательности правильно, главным образом потому, что я не могу обновить данные переменных до того, как все объекты будут созданы в базе данных (и, следовательно, переменные nil, которые должны быть обновлены), и мои обходные пути только приводят меня к неожиданным сбои и нелогичное поведение.
Вот мой код до сих пор:
В коде представления:
- (void)viewDidLoad{
[super viewDidLoad];
self.database = [DataHelper opendatabaseAndUseBlock:^{
[self setupFetchedResultsController]; // I pass this method trough a Block because I want it
// to be executed once the database is opened, not before.
}];
}
Затем я использую этот вспомогательный класс "DataHelper", который содержит следующий код:
@implementation DataHelper
// This method creates and opens the database, then calls the necessary methods
+ (UIManagedDocument *)openDatabaseAndUseBlock:(completion_block_t)completionBlock
{
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:@"Database"];
UIManagedDocument *database = [[UIManagedDocument alloc] initWithFileURL:url];
if (![[NSFileManager defaultManager] fileExistsAtPath:[database.fileURL path]]) {
[database saveToURL:database.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self loadDataIntodatabase:database withSuccess:success];
completionBlock();
}];
} else if (database.documentState == UIDocumentStateClosed) {
[database openWithCompletionHandler:^(BOOL success) {
[self loadDataIntodatabase:database withSuccess:success];
completionBlock();
}];
} else if (database.documentState == UIDocumentStateNormal) {
[self loadDataIntodatabase:database withSuccess:YES];
completionBlock();
}
return database;
}
// This method loads the "static" data into the database, by reading it from a plist file.
// Once the loading finishes, it should call the last method, for updating the variable data
+ (void)loadDataIntoDatabase:(UIManagedDocument *)database withSuccess:(BOOL)success
{
if (success) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
NSArray *plistData = [NSArray arrayWithContentsOfFile:path];
[database.managedObjectContext performBlock:^{
for (NSDictionary *data in plistData) {
[Object createObjectWithData:data inManagedObjectContext:database.managedObjectContext];
}
// Not sure what to do here!!
//[database saveToURL:database.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success) {
// [DataHelper updateVariableDataOfDatabase:database];
//}];
// Or...?
//[database updateChangeCount:UIDocumentChangeDone];
// [DataHelper updateVariableDataOfDatabase:database];
}];
} else {
NSLog(@"NO SUCCESS");
}
}
// This last method updates some parameters of the existing data
// in the database, to the ones found in another plist file
+ (void)updateVariableDataOfDatabase:(UIManagedDocument *)database
{
// This path is provisional, should be gotten from an online resource
NSString *path = [[NSBundle mainBundle] pathForResource:@"VariableData" ofType:@"plist"];
NSDictionary *variables = [NSDictionary dictionaryWithContentsOfFile:path];
[database.managedObjectContext performBlock:^{
// Update the objects changing the values of the variable parameters
// to the ones found in "variables"
// Now I should save the changes, or maybe not?
//[database saveToURL:database.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
// Or...
//[database updateChangeCount:UIDocumentChangeDone];
}];
}
@end
Если я использую метод "saveToURL", хотя он кажется неправильным, он выполняет последовательность загрузки правильно, потому что не выполняет последний метод, пока все данные не будут загружены в базу данных. Тем не менее, приложение аварийно завершает работу при попытке выполнить много операций (с большим количеством сбережений внутри, таких как удаление, обновление или перезагрузка).
С другой стороны, с помощью "updateChangeCount" приложение больше не падает, но последовательность не работает должным образом: переменные данные не обновляются, приложение не находит объект, который находится там, на база данных и тд. Если я подожду 2 минуты или около того между каждым действием, тогда да, это работает... но это не то, что я хочу.
Я бы очень признателен за помощь здесь, это отнимает у меня время, и крайний срок этого проекта довольно близко:(
Большое спасибо!
1 ответ
Прошло некоторое время, и, надеюсь, вы уже решили свои проблемы здесь, но на всякий случай, поскольку я сам боролся с кучей проблем, связанных с UIManagedDocument, за последние пару месяцев, поэтому я чувствую вашу боль. То, что вы делаете здесь, довольно похоже на то, что мне нужно было сделать в недавнем проекте.
К сожалению, документация и различные предложения, распространяющиеся в Интернете о том, как сохранить (или не сохранить) UIManagedDocument в целом, кажется, повсеместно. Я нашел похожие результаты, используя подходы saveToURL и updateChangeCount, однако подход updateChangeCount, похоже, реже приводит к ошибкам сохранения. Но я не думаю, что это действительно влияет на вашу проблему.
Если у вас есть только один поток, обращающийся к вашему документу, и если у вас есть только один документ, открытый для ваших данных, вам на самом деле не нужно его сохранять, он будет автоматически сохраняться в этом контексте. Сохранение необходимо для передачи изменений из этого контекста в другой контекст (в UIManagedDocument их два) и, в конечном итоге, в облако и другие устройства. У вас есть два уровня executeBlock в вашем завершение завершения, что может усложнять ситуацию и приводить к случаям, когда более чем один поток может одновременно обращаться к вашей базе данных, возможно, в непредсказуемом порядке. Когда я делаю всю свою предварительную загрузку и настройку, все это просто последовательно в рамкахручки завершения, за которым следует эквивалент вашего opendatabaseAndUseBlock, и он никогда не падает.