iCloud NSUbiquitousKeyValueStore初始同步/访问延迟 – 如何处理?
我正在使用NSUbiquitousKeyValueStore来存储一些应用程序设置。 我的逻辑是:当我在本地保存数据时,我把它保存到NSUbiquitousKeyValueStore也作为备份。 当我需要设置时,我在本地读取,如果在本地找不到数据(例如重新安装应用程序),则只使用iCloud键值存储。 如果用户有多个设备共享一个icloud ID,他可以在一台设备上写入设置并将它们下载到另一台(我警告他重写)。
我有一个奇怪的问题。 脚步:
- 安装一个应用程序,并将其数据保存到NSUbiquitousKeyValueStore。 确保数据在那里。
- 删除了应用程序(假设数据仍然存在于iCloud中)。
- 为了以防万一,等待几分钟,然后从Xcode内部安装并启动应用程序。
- 试图读取设置密钥使用[[NSUbiquitousKeyValueStore defaultStore] dataForKey:@“mykeyname”] – 有时可以,但有时键没有find!
- 等了15秒,再试一次。 成功。 困惑。
所以看起来像ios需要一些时间来为我的应用程序在dataForKey:call本地提供远程键值存储。 如果我写了这样一个系统(实际上我曾经 – 在不久之前,在另一个生活中),显然在询问和接收关键值数据之前必须有一个延迟。 所以我想通知一下:“我们在第一次启动时完成了下载/同步键值存储”或类似的事情。
据我所知,我可以在主线程中同步使用NSUbiquitousKeyValueStore(这对我来说很方便)。 但是[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]返回一个有效的url,然后我得到“key is not found”。 所以我不能依靠它。 有没有办法确保NSUbiquitousKeyValueStore工作是一个下载? 尤其是在networking速度慢的情况下
UPDATE
添加[[NSUbiquitousKeyValueStore defaultStore]同步](如在苹果文档中写的)初始化和加载是有点帮助。 还有很多问题,以iCloud。
昨天我已经成功地将数据保存到手机1上的键值存储,并在手机2上恢复。今天,我已经删除了手机2上的应用程序,并试图恢复数据。 但即使[[NSFileManager的defaultManager] URLForUbiquityContainerIdentifier:nil]返回有效的URL,我调用[[NSUbiquitousKeyValueStore defaultStore]同步]我调用dataForKey:MY_DATA_KEY时得到nil。
当我尝试从手机1(应用程序仍然安装)从icloud恢复数据成功,但是当我重新安装在这个手机上的应用程序恢复不成功了。
临时解决scheme是:“closuresiCloud->文档和数据 – closures和打开networking – 打开文档和数据”,但你也应该等待几分钟,然后它应该工作。
所以,问题:1.你有这样的问题与iCloud? 2.有什么办法可以找出数据不可用还是只是没有下载? 3.是否有任何已知的“延迟”iCloud? 我听说过7秒左右,但显然不是这样。 4.看起来应用程序不是非阻塞的iCloud数据更新速度非常快(秒),但是当您重新安装应用程序icloud需要几分钟才能实现键值存储。 有什么办法可以强制这个过程?
PS下面是我的CloudHelper供您参考 – 非常简单的c ++类,用于向/从iCloud键值存储中写入/读取二进制数据。 这是不可编译的,我已经适应了它有点更清晰 – 删除了我的引擎相关的代码。 如果您删除MySystem :: …调用,它仍然工作得很好。 除了我之前提到的。
class CloudHelper { public: static bool init(); static void deInit(); //save our data to iCloud with static int saveData(unsigned char* data, int from, int count); //get our data from iCloud static unsigned char * loadData(int *retsize, int * retint); //does iCloud work for us static bool isEnabled(); //do we have our key in iCloud static int isAvailable(); static const int RESULT_OK = 0; static const int RESULT_NO_CONNECTION = 1; static const int RESULT_NOT_FOUND = 2; static const int RESULT_SYNC_ERROR = 3; private: static bool enabled; static NSURL *ubiq; }; bool CloudHelper::enabled = false; NSURL *CloudHelper::ubiq = NULL; #define MY_DATA_KEY @"my_data_key" int CloudHelper::saveData(unsigned char* data, int from, int count) { if ([NSUbiquitousKeyValueStore defaultStore]) { NSData *d = [[[NSData alloc] initWithBytes:(data + from) length:count] autorelease]; [[NSUbiquitousKeyValueStore defaultStore] setData:d forKey: MY_DATA_KEY)]; if ([[NSUbiquitousKeyValueStore defaultStore] synchronize] != TRUE) return RESULT_SYNC_ERROR; return RESULT_OK; } return RESULT_NO_CONNECTION; } unsigned char * CloudHelper::loadData(int *retsize, int * retint) { if ([NSUbiquitousKeyValueStore defaultStore]) { [[NSUbiquitousKeyValueStore defaultStore] synchronize]; NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY]; if (d != NULL) { if (retsize != NULL) *retsize = d.length; if (retint != NULL) *retint = RESULT_OK; return d.bytes; } else { if (retsize != NULL) *retsize = -1; if (retint != NULL) *retint = RESULT_NOT_FOUND; } } else { if (retsize != NULL) *retsize = -1; if (retint != NULL) *retint = RESULT_NO_CONNECTION; } return NULL; } int CloudHelper::isAvailable() { int result = RESULT_NO_CONNECTION; if ([NSUbiquitousKeyValueStore defaultStore]) { [[NSUbiquitousKeyValueStore defaultStore] synchronize]; NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY]; if (d != NULL) result = RESULT_OK; else result = RESULT_NOT_FOUND; } else result = RESULT_NO_CONNECTION; return result; } void CloudHelper::deInit() { enabled = false; [ubiq release]; } bool CloudHelper::init() { enabled = false; NSURL *ubiq_ = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; [[NSUbiquitousKeyValueStore defaultStore] synchronize]; if (ubiq) { enabled = true; ubiq = [ubiq_ retain]; //save for further use } else { //is implemented elsewhere: this writes a local file with a counter, and if it is < REMINDER_COUNT allows us to show a warning to users bool allow = MySystem::isAllowToShowDialog(); if (allow) { //determines network state with Apple's Reachability if (!MySystem::isNetworkAvailable()) MySystem::showMessageBox(@"Network error"); //No network else MySystem::showMessageBox(@"You should log into your iCloud account to be able to backup your settings."); //No login } } return enabled; }
更新2
现在是2016.Android成为了ios的孪生兄弟,人类发现了引力波,Higgs获得了诺贝尔奖,微软收购了诺基亚,大家分别看到了Amber Heard和Jennifer Lawrence。 但是,iCloud仍然如此愚蠢。
最后,我在几个VPS上做了自己的一堆networking服务。 我拒绝使用第三方服务,因为他们大多数是不稳定和不可预测的。 然而我需要iCloud。 因为苹果的另一个死亡孩子不起作用。 SecKeyChain。 当我的游戏开始时,它的服务就消失 所以我决定在云中存储随机UUID,以便在重新安装之后区分用户(不再有设备标识)。 但是会出现什么问题呢? 一切! 我已经花了两天的时间让这个愚蠢的部署无误地进行部署,现在它不时地丢失了我的数据!
谢谢苹果,谢谢,谢谢,谢谢! LA-LA-LA! 太好啦! (马戏音乐的声音,消失在哭泣中)
结论
临时解决scheme是: – 在从键值存储获取数据之前调用同步 – 确保它能正常工作“closuresiCloud->文档和数据 – 在networking上closures和再次打开文档和数据”,而且还应该等待几分钟iCloud下载所有需要的数据
注意:当应用程序安装,已经工作(保存/加载)与键值存储更新的iCloud数据是非常快(7-15秒),但是当你重新安装应用程序似乎icloud需要几分钟来实现键 – 价值商店。
我会很高兴听到你的想法,因为icloud看起来几乎无法使用的function。 但是我不想build立我自己的服务器来获得相同的function。
我设置一个虚拟键NSUbiquitousKeyValueStore和调用应用程序启动同步。 结果不是100%的解决scheme,但稍好一些。 你可以试试这个
因为显然你的应用程序不应该挂在等待一个缓慢的networking。 这一切都在“ iClouddevise指南”中 。
注册NSUbiquitousKeyValueStoreDidChangeExternallyNotification
,调用 – NSUbiquitousKeyValueStoreDidChangeExternallyNotification
,并希望通知应该最终到达。
如果数据已经是最新的,我不认为你会收到通知,我不认为有一个很好的方式来知道数据是多less年。