简体   繁体   English

在app delegate中添加持久存储(启用iCloud)时崩溃

[英]Crash when adding persistent store (iCloud enabled) in app delegate

I am going to start updating this to help those seeking to use this as reference for their own personal code. 我将开始更新这个,以帮助那些寻求使用它作为他们自己的个人代码的参考。

Newest update 最新更新

  • I'm fairly sure I have found a way to resync devices back together once they have stopped talking to each other. 我很确定我已经找到了一种方法,一旦它们停止相互通信,就可以将设备重新同步。 I'm going to update my answer below with all of the details. 我将在下面用所有细节更新我的答案。 I thoroughly hope you all find this helpful. 我完全希望你们都觉得这很有帮助。 It's taken almost 2 months of trial and error to figure this one out. 花了将近2个月的试验和错误来解决这个问题。 So please reference and share this with others who are having similar issues getting devices to once again talk to each other through iCloud. 所以请参考并与其他有类似问题的人分享,让设备再次通过iCloud相互交谈。 It took me forever to figure this all out, so I am more than happy to save as many other developers as possible from having to create their own make-shift fixes. 我花了很长时间才弄清楚这一点,所以我非常乐意尽可能多地保存其他开发人员来创建自己的make-shift修复程序。

Another addition to help set up correctly 另一个有助于正确设置的补充

  • I found that after updating an app that has iCloud data associated with the account can cause a crash upon opening it because the iCloud data will attempt to merge immediately into the device (where the device has not yet set up its persistent store). 我发现在更新具有与帐户关联的iCloud数据的应用程序后,打开它会导致崩溃,因为iCloud数据将尝试立即合并到设备中(设备尚未设置其持久存储)。 I have now added @property (nonatomic, readwrite) BOOL unlocked; 我现在添加了@property (nonatomic, readwrite) BOOL unlocked; to AppDelegate.h and @synthesize unlocked; AppDelegate.h@synthesize unlocked; to AppDelegate.m . AppDelegate.m I then changed my - (NSPersistentStoreCoordinator *)persistentStoreCoordinator method as well as my - (void)mergeChangesFrom_iCloud method, both of which will be shown below (in the middle for the persistent store setup and at the bottom for the iCloud merge method). 然后我改变了我的- (NSPersistentStoreCoordinator *)persistentStoreCoordinator方法以及我的- (void)mergeChangesFrom_iCloud方法,这两种方法将在下面显示(在持久存储设置的中间和iCloud合并方法的底部)。 In essence, I am telling the app to prevent iCloud from merging data until the app has set up its persistent store. 本质上,我告诉应用程序阻止iCloud合并数据,直到应用程序设置其持久存储。 Otherwise, you will see the app crash due to unreadable faults. 否则,您将看到应用程序因无法读取的故障而崩溃。

Here is how I am setting up my persistentStoreCoordinator: 以下是我设置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> and <bundleIdentifier> are actual values, of course. <myAppKey><bundleIdentifier>是实际值。 I am just masking them for the purpose of sharing this code. 我只是为了共享这段代码而屏蔽它们。

I know that some people are still having troubles with this and may be using this question as reference on how to set up their own iCloud-enabled Core Data applications, so I want to update this whenever I make changes to my personal code, ensuring that all of you can use the code that works for me. 我知道有些人仍然遇到麻烦,并且可能正在使用这个问题作为如何设置他们自己的支持iCloud的核心数据应用程序的参考,所以每当我更改我的个人代码时我想更新它,确保所有人都可以使用适合我的代码。 In this update, I changed the initial cloudURL from [fileManager URLForUbiquityContainerIdentifier:@"<TeamIdentifier>.<bundleIdentifier>"] to [fileManager URLForUbiquityContainerIdentifier:nil] , ensuring that the container information is gathered from the entitlements file. 在此更新中,我将初始cloudURL从[fileManager URLForUbiquityContainerIdentifier:@"<TeamIdentifier>.<bundleIdentifier>"]更改为[fileManager URLForUbiquityContainerIdentifier:nil] ,确保从权利文件中收集容器信息。

Additional methods _notificationArray is defined as the following: @property (nonatomice, strong) NSMutableArray *notificationArray; 其他方法 _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;
}

Then there is the mergeiCloudChanges:forContext: method: 然后是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;
}

Initial problem 最初的问题

  • Using iCloud on iOS 5.0.1, I'm occasionally getting errors pertaining to the persistent store. 在iOS 5.0.1上使用iCloud,我偶尔会收到与持久存储有关的错误。 I'm going to continue updating this with new information as I find it through experimenting, but so far the solution I provided is the only way I can get the app working properly again (unfortunately jlstrecker's solution didn't work for me) once I start seeing the error, which is the following: 我将通过实验找到新信息继续更新,但到目前为止,我提供的解决方案是我可以让应用程序再次正常工作的唯一方法(不幸的是jlstrecker的解决方案对我不起作用)开始看到错误,如下所示:

    -NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:: CoreData: Ubiquity: Error attempting to read ubiquity root url: file://localhost/private/var/mobile/Library/Mobile%20Documents/./data/. -NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error :: CoreData:Ubiquity:尝试读取ubiquity root url时出错:file://localhost/private/var/mobile/Library/Mobile%20Documents/./data/。 Error: Error Domain=LibrarianErrorDomain Code=1 "The operation couldn't be completed. (LibrarianErrorDomain error 1 - Unable to initiate item download.)" UserInfo=0x176000 {NSURL=file://localhost/private/var/mobile/Library/Mobile%20Documents/./data/, NSDescription=Unable to initiate item download.} 错误:错误Domain = LibrarianErrorDomain Code = 1“操作无法完成。(LibrarianErrorDomain错误1 - 无法启动项目下载。)”UserInfo = 0x176000 {NSURL = file:// localhost / private / var / mobile / Library /Mobile%20Documents/./data/,NSDescription =无法启动项目下载。}

    For the life of me, I cannot figure out why I'm seeing this all the sudden or how to make it stop. 对于我的生活,我无法弄清楚为什么我突然看到这个或如何让它停止。 I have deleted the app from both devices, deleted the iCloud data which was previous syncing between them, and deleted any data from backups regarding the apps. 我从两台设备上删除了应用程序,删除了之前同步的iCloud数据,并删除了有关应用程序备份的所有数据。 I have restarted Xcode, restarted both devices, cleaned the Xcode project, yet nothing has stopped the error from showing up. 我重新启动了Xcode,重新启动了两个设备,清理了Xcode项目,但没有任何东西阻止错误显示。 I've never seen this error before and have had zero luck finding anything online on how to pin it down. 我以前从未见过这个错误,并且没有在线找到任何关于如何固定它的运气。

    The app crashes here: 该应用程序崩溃在这里:

     if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) { NSLog(@"bad things %@ %@", error, [error userInfo]); abort(); } 

    The log is never hit, nor is the abort. 日志永远不会被击中,也不会被中止。 I just see the error above and the app itself becomes unresponsive. 我只是看到上面的错误,应用程序本身变得没有响应。 If anyone can help point me in the right direction, I would be very appreciative. 如果有人能帮助我指出正确的方向,我将非常感激。

Previous issues/questions 以前的问题/问题

  • This seems to continue even after the update from the beta to the public release of 5.0.1. 即使从测试版更新到5.0.1的公开版本之后,这似乎仍在继续。 The last time it happened to me was after changing my managed context data model. 它最后一次发生在我之后是在更改我的托管上下文数据模型之后。 Considering I haven't released the app yet, I didn't bother merging a new version of the model. 考虑到我还没有发布应用程序,我没有费心合并新版本的模型。 I just deleted and reinstalled the app on my devices, but then it refused to cooperate with the data stored in the iCloud container, by which I mean that I received an error that the store could not download items. 我刚刚删除并重新安装了我的设备上的应用程序,但后来它拒绝配合存储在iCloud容器中的数据,我的意思是我收到了商店无法下载项目的错误。 I imagine this is due to conflicting data model types, which makes perfect sense. 我想这是由于数据模型类型的冲突,这是完全合理的。 So it seems you just need to get rid of the data within the iCloud container without getting rid of the container. 因此,您似乎只需要摆脱iCloud容器中的数据而不必删除容器。 Deleting the iCloud data seems to kill everything off, in essence disabling the container and App ID. 删除iCloud数据似乎会破坏所有内容,实质上是禁用容器和App ID。 Since it seemed simpler, I tried creating a new container as suggested by jlstrecker, but unfortunately, this didn't help at all. 由于它似乎更简单,我尝试按照jlstrecker的建议创建一个新容器,但不幸的是,这根本没有用。 So once again, I had to go through the steps I outlined in my answer, which again did the trick. 所以再一次,我必须完成我在答案中概述的步骤,这再次成功。 But considering how annoying it is to have to create new App IDs and update provisioning profiles each time, I thought it best to update what I've learned to potentially narrow down the cause and get to a quicker solution. 但考虑到每次必须创建新的App ID和更新配置文件是多么烦人,我认为最好更新我学到的内容,以便缩小原因并获得更快的解决方案。

    Going through iCloud > Storage & Backup > Manage Storage, then deleting the app would appear to be the best solution to empty the data, but doing this seems to corrupt the container, leading to the error above. 通过iCloud>存储和备份>管理存储,然后删除应用程序似乎是清空数据的最佳解决方案,但这样做似乎破坏了容器,导致上面的错误。 And after successfully doing this, no matter how many times I delete the app and reinstall it to the device (to make it appear like it's the first time appearing on the device and hopefully recreate the container), I can never get the app to show in the Documents & Data list again. 成功完成此操作后,无论我删除应用程序多少次并将其重新安装到设备上(使其看起来像第一次出现在设备上并希望重新创建容器),我永远无法让应用程序显示再次在“文档和数据”列表中。 This is somewhat concerning if it means that anyone who deletes data from their iCloud like that means that iCloud will not work for the app ever again. 这有点令人担忧,如果它意味着任何从他们的iCloud删除数据的人意味着iCloud将不再适用于该应用程序。 I am only using a development profile on the app so far, so perhaps using a distribution profile might make some difference, but I will have to test that before saying anything for certain. 到目前为止,我只在应用程序上使用开发配置文件,因此使用分发配置文件可能会有所不同,但我必须先测试一下,然后才能确定。

I hope these new updates help anyone who may be having trouble setting up their store. 我希望这些新的更新可以帮助任何可能在设置商店时遇到问题的人。 It has been working great for me so far. 到目前为止,它一直对我很有用。 I will be sure to update more if I find better fixes or just anything that makes the process more seemless. 如果我找到更好的解决方案,或者只是让过程更加无缝的任何事情,我一定会更新更新。

Updated answer to resync your devices Months of tinkering around have led me to figuring out what (I believe) the rooted problem is. 更新了重新同步设备的答案几个月的修修补补让我弄清楚(我相信)根深蒂固的问题是什么。 The issue has been getting devices to once again talk to each other after they fall out of sync. 问题是让设备在失去同步后再次互相交谈。 I can't say for sure what causes this, but my suspicion is that the transaction log becomes corrupted, or (more likely) the container of the log itself is recreated. 我不能确定是什么导致这种情况,但我怀疑是事务日志已损坏,或者(更有可能)重新创建日志容器。 This would be like device A posting changes to container A and device B doing the same as opposed to both posting to container C, where they can read/write to the logs. 这就像设备A发布更改到容器A和设备B执行相同操作,而不是发布到容器C,他们可以读取/写入日志。

Now that we know the problem, it's a matter of creating a solution. 现在我们知道了问题,这是一个创建解决方案的问题。 More tinkering led me to the following. 更多的修补让我得到了以下内容。 I have a method called resetiCloudSync:(BOOL)isSource , which is a modified version of the method above in my original question. 我有一个名为resetiCloudSync:(BOOL)isSource的方法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];
}

In this code, I have two distinct paths to take. 在这段代码中,我有两条不同的路径。 One is for devices which are not in sync and need to have data imported from the source device. 一种是用于不同步且需要从源设备导入数据的设备。 All that path does is clear the memory to prepare it for the data that is supposed to be in it. 所有这些路径都清楚了内存,以便为应该在其中的数据做好准备。

The other ( isSource = YES ) path, does a number of things. 另一个( isSource = YES )路径做了很多事情。 In general, it removes the corrupted container. 通常,它会删除损坏的容器。 It then creates a new container (for the logs to have a place to reside). 然后它创建一个新容器(让日志有一个驻留的位置)。 Finally, it searches through the parent entities and copies them. 最后,它搜索父实体并复制它们。 What this does is repopulate the transaction log container with the information that is supposed to be there. 这样做是使用应该存在的信息重新填充事务日志容器。 Then you need to remove the original entities so you don't have duplicates. 然后,您需要删除原始实体,以便您没有重复项。 Finally, reset the persistent store to "refresh" the app's core data and update all the views and fetchedResultsControllers . 最后,重置持久性存储以“刷新”应用程序的核心数据并更新所有视图和fetchedResultsControllers

I can attest that this works wonderfully. 我可以证明这很有效。 I've cleared the data from devices ( isSource = NO ) who have not talked to the primary device (where the data is held) for months. 我已经清除了几个月来没有与主设备(数据保存在哪里)通信的设备数据( isSource = NO )。 I then pushed the data from the primary device and delightfully watched as ALL my data appeared within seconds. 然后,我从主设备推送数据并愉快地观看,因为我的所有数据都在几秒钟内出现。

Again, please feel free to reference and share this to any and all who have had problems syncing with iCloud. 再次,请随意引用并分享给任何与iCloud同步有问题的人。

Answer to original question, which is no longer affected after iOS 5.1 came out, which fixed the crash after removing your app's iCloud storage in your Settings 原始问题的答案,在iOS 5.1发布后不再受影响,修复了在您的设置中删除应用程序的iCloud存储后崩溃

After many many many hours of trying anything and everything to get this sorted out, I tried creating a new App ID, updated the app's associated provisioning profile, changed around the iCloud container fields to match the new profile, and everything works again. 经过许多小时尝试任何事情和所有事情来解决这个问题后,我尝试创建一个新的App ID,更新应用程序的相关配置文件,在iCloud容器字段周围更改以匹配新配置文件,一切都可以再次运行。 I still have no idea why this happened, but it seems like the iCloud storage associated with that App ID got corrupted? 我仍然不知道为什么会发生这种情况,但似乎与该应用程序ID相关联的iCloud存储已损坏?

So bottom line is if this happens to anyone else, follow these steps and you should be good: 所以底线是如果其他人发生这种情况,请遵循以下步骤,你应该做得很好:

  1. Create a new App ID in the Provisioning Portal. 在Provisioning Portal中创建新的App ID。
  2. Find the provisioning profile associated with the app. 找到与该应用关联的配置文件。 Click Edit->Modify, then change the App ID to the one you just created. 单击编辑 - >修改,然后将应用程序ID更改为刚刚创建的应用程序ID。
  3. Submit the change, then replace the existing profile in Xcode with the one you just created. 提交更改,然后将Xcode中的现有配置文件替换为您刚刚创建的配置文件。
  4. Change all instances of <bundleIdentifier> to fit the new App ID (these would be in your main app Summary page, the entitlements for iCloud Containers and iCloud Key-Value Store, and in your AppDelegate file where you are creating the persistent store, like in my code above). 更改<bundleIdentifier>所有实例以适应新的App ID(这些将在您的主应用程序摘要页面,iCloud容器和iCloud Key-Value Store的权利,以及您正在创建持久性存储的AppDelegate文件中,例如在上面的代码中)。
  5. Restart Xcode since you changed information regarding provisioning profiles (it will complain otherwise and refuse to run on the device). 由于您更改了有关配置文件的信息,因此重新启动Xcode(否则会报告并拒绝在设备上运行)。
  6. Ensure that the new profile is on the devices you wish to install the app on, then build and run. 确保新配置文件位于您要安装应用程序的设备上,然后构建并运行。 Everything should work just fine at this point. 在这一点上,一切都应该工作得很好。

Another clarification: A similar situation happened to me testing devices on iOS 6.0.1 and the 6.1 beta 2. 另一个澄清:在iOS 6.0.1和6.1 beta 2上测试设备时发生了类似的情况。

It is not completely fixed in iOS 5.1 as stated by @Slev. 如@Slev所述,它在iOS 5.1中并未完全修复。 One device would completely freeze for roughly 80 seconds while trying to access the persistent store on iCloud but would never actually access the information stored there. 一个设备在尝试访问iCloud上的持久存储时会完全冻结大约80秒,但实际上永远不会访问存储在那里的信息。

I believe this is due to a corrupted log file in the device's OS. 我相信这是由于设备操作系统中的日志文件损坏。 Deleting the app or iCloud data on the device did nothing to fix the freeze/inability to access the iCloud store. 删除设备上的应用程序或iCloud数据无法解决冻结/无法访问iCloud存储的问题。

The one fix I found was to reset all settings (erase all content works too) on the device. 我发现的一个修复是reset all settings (擦除所有内容也可以)。 settings->general->reset.

Only then could I access the data on iCloud with my app on that device again. 只有这样我才能再次使用我的应用程序访问iCloud上的数据。 I hope this helps anyone else that came here looking for a solution to a very frustrating bug. 我希望这有助于其他任何人来到这里寻找一个非常令人沮丧的bug的解决方案。

I got this error while using one device on 5.0.1 beta 1 and one on 5.0.0. 我在5.0.1 beta 1和5.0.0上使用一个设备时出现此错误。

I got rid of the error by changing the name of the iCloud Container. 我通过更改iCloud容器的名称来消除错误。 In the iCloud Containers list, the first one has to match your app ID, but you can add additional containers with different names. 在iCloud容器列表中,第一个必须与您的应用程序ID匹配,但您可以添加具有不同名称的其他容器。

(Like slev's solution of changing the app ID, this is only a good solution if the app hasn't been released yet.) (就像slev改变应用ID的解决方案一样,如果应用尚未发布,这只是一个很好的解决方案。)

Excuse me Slev, I've got the same problem that you have when you say that the app is crashing because the iCloud data will attempt to merge immediately into the device (where the device has not yet set up its persistent store) but I can't understand how have you solved this. 对不起Slev,当你说应用程序崩溃时,我遇到了同样的问题,因为iCloud数据会尝试立即合并到设备中(设备尚未设置其持久存储)但我可以不明白你是怎么解决这个问题的。

Your code is : 你的代码是:

- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
    if (self.unlocked) {
       NSManagedObjectContext *moc = [self managedObjectContext];
        [moc performBlock:^{
            [self mergeiCloudChanges:notification forContext:moc];
        }];
    }
}

I haven't tried it yet but looking at this code I wonder what happen to the notifications that arrive when "unlocked" is false. 我还没有尝试过但是看看这段代码我想知道当“解锁”是假的时候到达的通知会发生什么。 Would you loose them? 你会松开它们吗?

Wouldn't it be better to have a while loop that test the "unlocked" property and spend some timespan until the property becomes true? 拥有一个测试“未锁定”属性的while循环并花费一些时间直到属性变为真是不是更好?

I hope you will understand my very bad english... :) Thank you 我希望你能理解我非常糟糕的英语...... :)谢谢
Dave 戴夫

UPDATE: 更新:

Everyone should really take a look at the iCloud Core Data session 227 from WWDC 2012. The source code they provide is an excellent starting point for an iCloud based solution. 每个人都应该真正看一下WWDC 2012的iCloud核心数据会话227.他们提供的源代码是基于iCloud的解决方案的绝佳起点。 Really take the time to go through what they are doing. 真的花时间去完成他们正在做的事情。 You will need to fill in some holes like copying objects from one store to another and de-duping. 您需要填写一些漏洞,例如将对象从一个商店复制到另一个商店并进行重复数据删除。 That being said, I am no longer using the migratePersistentStore approach as described below for moving between local and iCloud stores. 话虽这么说,我不再使用如下所述的migratePersistentStore方法在本地和iCloud存储之间移动。


My original answer: 我的原始答案:

Slev asked me to post some code for migrating a store from a local copy to iCloud and back again. Slev让我发布了一些代码,用于将商店从本地副本迁移到iCloud并再次返回。 This code is experimental and should not be used in production. 此代码是实验性的,不应在生产中使用。 It is only provided here as reference for us to share and move forward. 它仅作为参考提供给我们分享和前进。 Once Apple releases a proper reference application, you should probably consult that for your patterns and practices. 一旦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];
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM