【发布时间】:2013-11-30 01:43:22
【问题描述】:
我正在编写一个针对 iOS7 的应用程序。
我的测试设备是:
1) iPhone 5 16GB
2) iPad 第三代 16GB Wi-fi+Cellular。
我对 UIManagedDocument 和 iCloud 感到很生气。
我阅读了我找到的所有文档,观看了斯坦福 CS193P 视频,并且正在关注 Erica Sadun 的一些代码。
我无法解决的问题是,在创建本地 CoreData 文件后,我无法重新打开或保存它,因为它在 [Closed | SavingError] 状态。
打开并准备好它对我来说非常重要,因为:
1) 用户第一次启动应用程序时,它会用演示数据填充数据库。
2) 演示数据创建后,用户可以创建一些新数据,但如果文件处于此状态,则无法保存创建的数据。
3) 如果文档处于 SavingError 状态,演示数据将不会被保存。
应用程序中的第一个 TableViewController 调用 fetchDataWithBlock:。block 是一个简单的标准 NSFetchRequest。
这是我用来为 UIManagedDocument 设置 PSC 选项的代码,并按照 Erica 的建议创建和打开它(创建、关闭、重新打开)。里面有很多登录信息,以便更好地了解发生了什么。
#define DB_LOCAL_FILE_NAME @"CoreDataLocalFile"
#define DB_TRANSACTIONS_LOG_FILE_NAME @"TransactionsLog"
-(NSURL *) dbLocalDirectory
{
//Returns the application's document directory
_dbLocalDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
return _dbLocalDirectory;
}
-(NSURL*) iCloudURL
{
_iCloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
return _iCloudURL;
}
-(NSURL*) iCloudDataLogFilesURL
{
_iCloudDataLogFilesURL = [self.iCloudURL URLByAppendingPathComponent:@"CoreDataTransactionsLog"];
//If the folder is not present, create it.
if(![[NSFileManager defaultManager] fileExistsAtPath:_iCloudDataLogFilesURL.path]){
NSLog(@"Creating the iCloudDataLogFilesURL: %@", _iCloudDataLogFilesURL);
NSError *error;
if(![[NSFileManager defaultManager] createDirectoryAtPath:_iCloudDataLogFilesURL.path withIntermediateDirectories:YES attributes:nil error:&error]){
NSLog(@"ERROR creating iCloud folder: %@", error.localizedFailureReason);
}
}
return _iCloudDataLogFilesURL;
}
-(UIManagedDocument *) managedDocument
{
//Returns the database ManagedDocument
if (!_managedDocument){
//Init the document
_managedDocument = [[UIManagedDocument alloc] initWithFileURL:
[self.dbLocalDirectory URLByAppendingPathComponent:DB_LOCAL_FILE_NAME]];
}
return _managedDocument;
}
-(void) setPersistentStoreOptionsInDocument: (UIManagedDocument*) document
{
if(self.iCloudDataLogFilesURL){
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:DB_TRANSACTIONS_LOG_FILE_NAME
forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:self.iCloudDataLogFilesURL
forKey:NSPersistentStoreUbiquitousContentURLKey];
[options setObject:[NSNumber numberWithBool:YES]
forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES]
forKey:NSInferMappingModelAutomaticallyOption];
document.persistentStoreOptions = options;
//if file exists, use contents of document.fileURL/@"DocumentsMetadata.plist" instead
NSLog(@"Using iCLoud with PSC Options: %@", document.persistentStoreOptions);
}else{
NSLog(@"ERROR. Can't add iCloud options because I don't have a valid iCloudDataLogFilesURL.");
}
}
-(void) fetchDataWithBlock: (void (^) (void)) fetchingDataBlock
{
//If the CoreData local file exists then open it and perform the query
if([[NSFileManager defaultManager] fileExistsAtPath:[self.managedDocument.fileURL path]]){
NSLog(@"The CoreData local file in the application sandbox already exists. I am opening it.");
[self.managedDocument openWithCompletionHandler:^(BOOL success) {
if(success){
NSLog(@"SUCCESS: The CoreData local file has been opened succesfully. Fetching data.");
fetchingDataBlock();
}else{
NSLog(@"ERROR: Can't open the CoreData local file. Can't fetch the data.");
NSLog(@"%@", self.managedDocument);
return;
}
}];
}else{
NSLog(@"The CoreData local file in the application sandbox did not exist.");
//1. Create the Core Data local File
//----> Set the iCloud options
[self setPersistentStoreOptionsInDocument:_managedDocument];
[self.managedDocument saveToURL:self.managedDocument.fileURL
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if(success){
//2. Close the Core Data local File
NSLog(@"SUCCESS: I created the CoreData local file in the application sandbox. Now I am closing it.");
[self.managedDocument closeWithCompletionHandler:^(BOOL success) {
if(success){
//3. Reopen the Core Data local File
NSLog(@"SUCCESS: I closed the CoreData local file just created. Now I am reopening it.");
[self.managedDocument openWithCompletionHandler:^(BOOL success) {
if(success){
NSLog(@"SUCCESS: I reopened the CoreData local file just created. Fetching data.");
fetchingDataBlock();
}else{
NSLog(@"ERROR: Can't reopen the CoreData local file just created and then closed. Can't fetch the data.");
NSLog(@"%@", self.managedDocument);
return;
}
}];
}else{
NSLog(@"ERROR: Can't close the CoreData local file just created. Can't fetch the data.");
NSLog(@"%@", self.managedDocument);
return;
}
}];
}else{
NSLog(@"ERROR: Can't create the CoreData local file in the application sandbox. Can't fetch the data.");
NSLog(@"%@", self.managedDocument);
return;
}
}];
}
}
这些是日志:
2013-11-16 18:19:18.731 当前 iCloud 令牌:
2013-11-16 18:19:19.001 将 iCLoud 与 PSC 选项一起使用:{ NSInferMappingModelAutomaticallyOption = 1; NSMigratePersistentStoresAutomaticallyOption = 1; NSPersistentStoreUbiquitousContentNameKey = "TransactionsLog"; NSPersistentStoreUbiquitousContentURLKey = "file:///private/var/mobile/Library/Mobile%20Documents/XXX/CoreDataTransactionsLog/"; }
2013-11-16 18:19:19.003 应用沙箱中的 CoreData 本地文件不存在。
2013-11-16 18:19:19.032 ApplicationDidBecomeActive
2013-11-16 18:19:19.313 -PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:: CoreData: Ubiquity: mobile~D5AEDBB6-EEFC-455C-A52C-91ADDC1BE081:TransactionsLog 使用本地存储:1
2013-11-16 18:19:19.771 成功:我在应用程序沙箱中创建了 CoreData 本地文件。现在我要关闭它了。
2013-11-16 18:19:19.778 成功:我关闭了刚刚创建的 CoreData 本地文件。现在我重新打开它。
2013-11-16 18:19:20.073 错误:无法重新打开刚刚创建然后关闭的 CoreData 本地文件。无法获取数据。
2013-11-16 18:19:20.074 fileURL: file:///var/mobile/Applications/07702036-765D-414C-9E8B-D4C2B4055CB8/Documents/CoreDataLocalFile documentState: [关闭 |保存错误]
如您所见,documentState 是:[Closed |保存错误]
如果我重新启动应用程序,它会毫无问题地打开数据库,文档状态为正常。 问题是我需要在第一次运行应用程序并创建演示数据时打开并准备好它。然后必须立即使用 UIDocumentsaveForOverwriting 选项保存文件。
我不能要求用户退出应用程序然后重新启动它或丢失所有 dem 数据,因为文件不会被保存!
为了更好地理解发生了什么,我将 UIManagedDocument 子类化以覆盖 handleError:userInteractionPermitted:.
它告诉我文档有这个错误状态,因为“Error Domain=NSCocoaErrorDomain Code=134311 “提供的通用名称已在使用中。”。
2013-11-17 18:33:50.214 [370:1803] UIManagedDocument 中的错误:错误域 = NSCocoaErrorDomain 代码 = 134311“提供的通用名称已在使用中。” UserInfo=0x14edadf0 {NSLocalizedDescription=提供的通用名称已在使用中。,NSURL=file:///var/mobile/Applications/A0B371E0-C992-4D57-895A-E2CCB8A35367/Documents/CoreDataLocalFile/StoreContent.nosync/CoreDataUbiquitySupport/mobile ~0EDD3A67-63F4-439F-A055-A13808949BBD/TransactionsLog/A9728F87-0F79-4FE3-9B76-AABD3950BB67/store/persistentStore, NSPersistentStoreUbiquitousContentNameKey=TransactionsLog}
其他信息:
如果我搬家:
//设置 iCloud 选项
[self setPersistentStoreOptionsInDocument:_managedDocument];
使用方法 saveToURL:forSaveOperation:completionHandler: 从之前到之后创建本地文件,然后 UIManagedDocument 正确重新打开,但每当我尝试使用用于创建但值 UIDocumentSaveForOverwriting 的相同方法保存它时,它都会触发错误代码 134030对于 forSaveOperation 选项。如果用 saveToURL:forSaveOperation:completionHandler: 我使用 [self.managedDocument.managedObjectContext save:nil] 覆盖文件,也会发生同样的情况
注 1:我注释掉了所有的演示数据创建代码,以确保它不是罪魁祸首。一旦空白的 UIManagedDocument 在创建后成功重新打开,我就会尝试将其保存覆盖。
注 2:从 UIManagedDocument(本地文件)DocumentMetaData.plist 中获取选项表明唯一的选项集是 NSPersistentStoreUbiquitousContentNameKey。这很奇怪,因为我在创建文件后设置了 4 个不同的选项。
关于如何解决这个问题的任何提示?
谢谢
尼古拉
【问题讨论】:
-
过多的代码转储。请综合。
-
很难理解这里发生了什么。但是,如果文档在重新启动后正常打开,那么我会怀疑保存/关闭/打开是异步发生的,并且重新打开正在踩着保存/关闭。有没有可以使用的回调或等待?
-
UIManagedDocument 的关闭和重新打开都是通过标准回调异步处理的。我相信真正的问题可能隐藏在 PSC 选项中。
标签: objective-c core-data ios7 icloud uimanageddocument