我将开始更新此内容,以帮助那些寻求将其用作个人代码参考的人。
最新更新
帮助正确设置的另一个补充
@property (nonatomic, readwrite) BOOL unlocked;
到AppDelegate.h
和@synthesize unlocked;
到AppDelegate.m
。然后我将-
(NSPersistentStoreCoordinator *)persistentStoreCoordinator
方法更改为
以及我的- (void)mergeChangesFrom_iCloud
方法,两者都有
将在下面显示(在持久存储设置的中间和
在iCloud合并方法的底部)。从本质上讲,我在说
应用程序,以防止iCloud合并数据,直到应用程序设置
它的持久存储。否则,您将看到应用程序崩溃
不可读的错误。以下是我设置persistentStoreCoordinator的方法:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
// here is where you declare the persistent store is not prepared;
self.unlocked = NO;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Maintain_My_Car.sqlite"];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *options = nil;
NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"data"];
if (coreDataCloudContent.length != 0) {
// iCloud enabled;
cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, @"<bundleIdentifier>.store", NSPersistentStoreUbiquitousContentNameKey, cloudURL, NSPersistentStoreUbiquitousContentURLKey, nil];
} else {
// iCloud not enabled;
options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
}
NSError *error = nil;
[psc lock];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(@"bad things %@ %@", error, [error userInfo]);
abort();
}
[psc unlock];
// the store is now prepared and ready for iCloud to import data;
self.unlocked = YES;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"iCloud persistent store added");
[[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil];
});
});
return __persistentStoreCoordinator;
}
当然, <myAppKey>
和<bundleIdentifier>
是实际值。我只是为了共享这段代码而屏蔽它们。
我知道有些人仍然遇到麻烦,并且可能正在使用这个问题作为如何设置他们自己的支持iCloud的核心数据应用程序的参考,所以每当我更改我的个人代码时我想更新它,确保所有人都可以使用适合我的代码。在此更新中,我将初始cloudURL从[fileManager URLForUbiquityContainerIdentifier:@"<TeamIdentifier>.<bundleIdentifier>"]
更改为[fileManager URLForUbiquityContainerIdentifier:nil]
,确保从权利文件中收集容器信息。
其他方法
_notificationArray
定义如下:
@property (nonatomice, strong) NSMutableArray *notificationArray;
@synthesize notificationArray = _notificationArray;
- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
if (self.unlocked) {
NSManagedObjectContext *moc = [self managedObjectContext];
if (self.notificationArray.count != 0) {
for (NSNotification *note in _notificationArray) {
[moc performBlock:^{
[self mergeiCloudChanges:note forContext:moc];
}];
}
[_notificationArray removeAllObjects];
[moc performBlock:^{
[self mergeiCloudChanges:notification forContext:moc];
}];
} else {
[moc performBlock:^{
[self mergeiCloudChanges:notification forContext:moc];
}];
}
} else {
if (_notificationArray == nil) {
_notificationArray = [[NSMutableArray alloc] init];
}
[_notificationArray addObject:notification];
}
}
- (void)resetStore {
[self saveContext];
__persistentStoreCoordinator = nil;
__managedObjectContext = nil;
// reset the managedObjectContext for your program as you would in application:didFinishLaunchingWithOptions:
myMainView.managedObjectContext = [self managedObjectContext];
// the example above will rebuild the MOC and PSC for you with the new parameters in mind;
}
然后是mergeiCloudChanges:forContext:
方法:
- (void)mergeiCloudChanges:(NSNotification *)note forContext:(NSManagedObjectContext *)moc {
// below are a few logs you can run to see what is being done and when;
NSLog(@"insert %@", [[note userInfo] valueForKey:@"inserted"]);
NSLog(@"delete %@", [[note userInfo] valueForKey:@"deleted"]);
NSLog(@"update %@", [[note userInfo] valueForKey:@"updated"]);
[moc mergeChangesFromContextDidSaveNotification:note];
NSNotification *refreshNotification = [NSNotification notificationWithName:@"RefreshAllViews" object:self userInfo:[note userInfo]];
[[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
// do any additional work here;
}
初始问题
在iOS 5.0.1上使用iCloud时,我偶尔会收到相关错误 到持久性商店。我将继续更新此内容 我通过实验发现它的新信息,但到目前为止 我提供的解决方案是我可以让应用程序正常工作的唯一方法 再一次(不幸的是,jlstrecker的解决方案并没有起作用 我)一旦我开始看到错误,这是以下内容:
-NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error :: CoreData:Ubiquity:尝试读取ubiquity根URL时出错: 文件://localhost/private/var/mobile/Library/Mobile%20Documents/./data/。 错误:错误Domain = LibrarianErrorDomain Code = 1&#34;操作 无法完成。 (LibrarianErrorDomain错误1 - 无法执行 启动项目下载。)&#34;的UserInfo = 0x176000 {NSURL =文件://localhost/private/var/mobile/Library/Mobile%20Documents/./data/, NSDescription =无法启动项目下载。}
对于我的生活,我无法弄清楚为什么我会看到这一切 突然或如何让它停止。我已从两者中删除了该应用 设备,删除之前同步的iCloud数据 它们,并删除了有关应用程序的备份中的任何数据。我有 重新启动Xcode,重新启动两个设备,清理Xcode项目, 但没有任何东西可以阻止错误出现。我从未见过 之前的这个错误,并没有运气在网上找到任何东西 如何把它固定下来。
应用程序崩溃了:
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(@"bad things %@ %@", error, [error userInfo]);
abort();
}
日志永远不会被击中,也不会被中止。我只看到上面的错误 并且应用程序本身变得反应迟钝。如果有人能指点我 在正确的方向,我将非常感激。
以前的问题/问题
即使在从测试版到更新版之后,这似乎仍在继续 公开发布5.0.1。最后一次发生在我身上的事情发生了 更改我的托管上下文数据模型。考虑到我没有 发布了应用程序,我没有打扰合并新版本的 模型。我刚刚删除并重新安装了我的设备上的应用程序,但是 然后它拒绝配合存储在iCloud中的数据 容器,我的意思是我收到了商店的错误 无法下载项目。我想这是由于数据冲突造成的 模型类型,这是完美的意义。所以看来你只需要 摆脱iCloud容器内的数据而不会摆脱 容器。删除iCloud数据似乎会杀死所有内容 off,实质上是禁用容器和App ID。看起来好像 更简单,我尝试按照jlstrecker的建议创建一个新容器, 但不幸的是,这根本没有帮助。所以我必须再次这样做 完成我在答案中概述的步骤,再次完成了 特技。但考虑到创建新App有多烦人 每次ID和更新配置文件,我认为最好 更新我所学到的可能缩小原因并得到的东西 更快的解决方案。
通过iCloud&gt;存储&amp;备份&gt;然后管理存储 删除应用程序似乎是清空应用程序的最佳解决方案 数据,但这样做似乎腐蚀了容器,导致了 上面的错误。成功完成后,无论多少 我删除应用程序并将其重新安装到设备(以实现它 看起来像是第一次出现在设备上,希望如此 重新创建容器),我永远无法让应用程序显示在 文件&amp;数据列表再次。如果这意味着,这有点令人担忧 任何从他们的iCloud删除数据的人都意味着这一点 iCloud将不再适用于该应用程序。我只用了一个 到目前为止,应用程序上的开发配置文件,所以也许使用 分布情况可能会有所不同,但我必须这样做 在说出任何肯定的事之前先测试一下。
我希望这些新的更新可以帮助任何可能在设置商店时遇到问题的人。到目前为止,它一直对我很有用。如果我找到更好的修复方法,或者只是让这个过程变得更加无缝的任何东西,我一定会更新更新。
答案 0 :(得分:13)
更新了重新同步设备的答案 几个月的修补让我弄清楚(我相信)根深蒂固的问题是什么。问题是让设备在失去同步后再次互相交谈。我不能确定是什么导致这种情况,但我怀疑是事务日志已损坏,或者(更有可能)重新创建日志容器。这就像设备A发布更改到容器A和设备B做同样的事情而不是发布到容器C,他们可以读取/写入日志。
现在我们知道了这个问题,这是一个创建解决方案的问题。更多的修补让我得到了以下内容。我有一个名为resetiCloudSync:(BOOL)isSource
的方法,它是我原始问题中上述方法的修改版本。
- (void)resetiCloudSync:(BOOL)isSource {
NSLog(@"reset sync source %d", isSource);
NSManagedObjectContext *moc = self.managedObjectContext;
if (isSource) {
// remove data from app's cloud account, then repopulate with copy of existing data;
// find your log transaction container;
NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"store"];
cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
NSError *error = nil;
// remove the old log transaction container and it's logs;
[[NSFileManager defaultManager] removeItemAtURL:cloudURL error:&error];
// rebuild the container to insert the "new" data into;
if ([[NSFileManager defaultManager] createFileAtPath:coreDataCloudContent contents:nil attributes:nil]) {
// this will differ for everyone else. here i set up an array that stores the core data objects that are to-many relationships;
NSArray *keyArray = [NSArray arrayWithObjects:@"addedFields", @"mileages", @"parts", @"repairEvents", nil];
// create a request to temporarily store the objects you need to replicate;
// my heirarchy starts with vehicles as parent entities with many attributes and relationships (both to-one and to-many);
// as this format is a mix of just about everything, it works great for example purposes;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Vehicle" inManagedObjectContext:moc];
[request setEntity:entity];
NSError *error = nil;
NSArray *vehicles = [moc executeFetchRequest:request error:&error];
for (NSManagedObject *object in vehicles) {
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:object.entity.name inManagedObjectContext:moc];
// check regular values;
for (NSString *key in object.entity.attributesByName.allKeys) {
[newObject setValue:[object valueForKey:key] forKey:key];
}
// check relationships;
NSMutableSet *relSet = [[NSMutableSet alloc] init];
for (NSString *key in object.entity.relationshipsByName.allKeys) {
[relSet removeAllObjects];
// check to see relationship exists;
if ([object valueForKey:key] != nil) {
// check to see if relationship is to-many;
if ([keyArray containsObject:key]) {
for (NSManagedObject *toManyObject in [object valueForKey:key]) {
[relSet addObject:toManyObject];
}
} else {
[relSet addObject:[object valueForKey:key]];
}
// cycle through objects;
for (NSManagedObject *subObject in relSet) {
NSManagedObject *newSubObject = [NSEntityDescription insertNewObjectForEntityForName:subObject.entity.name inManagedObjectContext:moc];
// check sub values;
for (NSString *subKey in subObject.entity.attributesByName.allKeys) {
NSLog(@"subkey %@", subKey);
[newSubObject setValue:[subObject valueForKey:subKey] forKey:subKey];
}
// check sub relationships;
for (NSString *subRel in subObject.entity.relationshipsByName.allKeys) {
NSLog(@"sub relationship %@", subRel);
// set up any additional checks if necessary;
[newSubObject setValue:newObject forKey:subRel];
}
}
}
}
[moc deleteObject:object];
}
[self resetStore];
}
} else {
// here we remove all data from the current device to populate with data pushed to cloud from other device;
for (NSManagedObject *object in moc.registeredObjects) {
[moc deleteObject:object];
}
}
[[[UIAlertView alloc] initWithTitle:@"Sync has been reset" message:nil delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil] show];
}
在这段代码中,我有两条不同的路径。一种是用于不同步且需要从源设备导入数据的设备。所有这些路径都清楚了内存,以便为应该在其中的数据做好准备。
另一个(isSource = YES
)路径做了很多事情。通常,它会删除损坏的容器。然后它创建一个新容器(让日志有一个驻留的位置)。最后,它搜索父实体并复制它们。这样做是使用应该存在的信息重新填充事务日志容器。然后,您需要删除原始实体,以便您没有重复项。最后,重置持久性存储以“刷新”应用程序的核心数据并更新所有视图并fetchedResultsControllers
。
我可以证明这很有效。我已经清除了几个月来没有与主设备(数据所在的设备)通信的设备(isSource = NO
)的数据。然后,我从主设备推送数据并愉快地观看,因为我的所有数据都在几秒钟内出现。
同样,请随意引用并分享给与iCloud同步的任何人。
回答原始问题,iOS 5.1问世后不再受影响,修复了在设置中移除应用的iCloud存储空间后的崩溃
经过多长时间尝试任何事情和所有事情才能解决这个问题,我尝试创建一个新的App ID,更新应用程序的相关配置文件,在iCloud容器字段周围更改以匹配新配置文件,一切都再次运行。我仍然不知道为什么会发生这种情况,但似乎与该应用程序ID相关联的iCloud存储已损坏?
所以,如果这种情况发生在其他任何人身上,那么请按照以下步骤操作,你应该做得很好:
<bundleIdentifier>
的所有实例以适应新的应用ID(这些将位于您的主应用摘要页面,iCloud容器和iCloud键值存储的权利以及您正在创建的AppDelegate文件中)持久存储,就像我上面的代码一样。)答案 1 :(得分:7)
另一个澄清:我在iOS 6.0.1和6.1 beta 2上测试设备时发生了类似的情况。
如@Slev所述,它在iOS 5.1中并未完全修复。一个设备在尝试访问iCloud上的持久存储时会完全冻结大约80秒,但实际上永远不会访问存储在那里的信息。
我认为这是由于设备操作系统中的日志文件已损坏。删除设备上的应用程序或iCloud数据无法解决冻结/无法访问iCloud存储的问题。
我发现的一个修复是reset all settings
(擦除所有内容也可以)。 settings->general->reset.
只有这样我才能再次使用我的应用程序在iCloud上访问该数据。我希望这有助于其他任何人来到这里寻找一个非常令人沮丧的bug的解决方案。
答案 2 :(得分:3)
我在5.0.1 beta 1和5.0.0上使用一个设备时遇到此错误。
我通过更改iCloud容器的名称来消除错误。在iCloud容器列表中,第一个必须与您的应用程序ID匹配,但您可以添加具有不同名称的其他容器。
(就像slev改变应用ID的解决方案一样,如果应用还没有发布,这只是一个很好的解决方案。)
答案 3 :(得分:0)
<强>更新强>
每个人都应该真正看一下来自WWDC 2012的iCloud核心数据会话227.他们提供的源代码是基于iCloud的解决方案的优秀起点。真的花时间去完成他们正在做的事情。您需要填写一些漏洞,例如将对象从一个商店复制到另一个商店并进行重复数据删除。话虽这么说,我不再使用如下所述的migratePersistentStore
方法在本地和iCloud商店之间移动。
我原来的回答:
Slev让我发布一些代码,用于将商店从本地副本迁移到iCloud,然后再返回。 此代码是实验性的,不应在生产中使用。 此处仅提供我们分享和推进的参考。一旦Apple发布了适当的参考应用程序,您可能应该参考您的模式和实践。
-(void) onChangeiCloudSync
{
YourAppDelegate* appDelegate = (YourAppDelegate*) [[UIApplication sharedApplication] delegate];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([iCloudUtility iCloudEnabled])
{
NSURL *storeUrl = [[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp2.sqlite"];
NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"data"];
cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
// The API to turn on Core Data iCloud support here.
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@"com.yourcompany.yourapp.coredata", NSPersistentStoreUbiquitousContentNameKey, cloudURL, NSPersistentStoreUbiquitousContentURLKey, [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];
NSPersistentStore* store = [appDelegate.persistentStoreCoordinator.persistentStores objectAtIndex:0];
NSError* error;
if (![appDelegate.persistentStoreCoordinator migratePersistentStore:store toURL:storeUrl options:options withType:NSSQLiteStoreType error:&error])
{
NSLog(@"Error migrating data: %@, %@", error, [error userInfo]);
//abort();
}
[fileManager removeItemAtURL:[[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp.sqlite"] error:nil];
[appDelegate resetStore];
}
else
{
NSURL *storeUrl = [[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp.sqlite"];
// The API to turn on Core Data iCloud support here.
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
NSPersistentStore* store = [appDelegate.persistentStoreCoordinator.persistentStores objectAtIndex:0];
NSError* error;
if (![appDelegate.persistentStoreCoordinator migratePersistentStore:store toURL:storeUrl options:options withType:NSSQLiteStoreType error:&error])
{
NSLog(@"Error migrating data: %@, %@", error, [error userInfo]);
//abort();
}
[fileManager removeItemAtURL:[[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp2.sqlite"] error:nil];
[appDelegate resetStore];
}
}
答案 4 :(得分:0)
对不起Slev,当你说应用程序崩溃时,我遇到了同样的问题,因为iCloud数据会尝试立即合并到设备中(设备尚未设置其持久存储)但我无法理解你是如何解决这个问题的。
您的代码是:
- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
if (self.unlocked) {
NSManagedObjectContext *moc = [self managedObjectContext];
[moc performBlock:^{
[self mergeiCloudChanges:notification forContext:moc];
}];
}
}
我还没有尝试过,但是看看这段代码,我想知道当“unlocked”为假时到达的通知会发生什么。你会松开它们吗?
使用while循环测试“unlocked”属性并花费一些时间直到属性变为真是不是更好?
我希望你能理解我非常糟糕的英语...... :) 谢谢你 戴夫