iCloud NSUbiquitousKeyValueStore初始同步/访问延迟 - 如何处理?

时间:2012-09-21 23:11:45

标签: iphone ios icloud-api nsubiquitouskeyvaluestore

我正在使用NSUbiquitousKeyValueStore来存储一些应用程序设置。我的逻辑是:当我在本地保存数据时,我还将其保存到NSUbiquitousKeyValueStore作为备份。当我需要设置时,我在本地阅读,如果没有在本地找到数据,我只使用iCloud键值存储(例如,在重新安装应用程序之后)。如果用户有多个设备共享一个icloud id,他可以在一个设备上写设置并将其下载到另一个设备(我警告他重写)。

我有一个奇怪的问题。步骤进行:

  1. 安装了一个应用并将其数据保存到NSUbiquitousKeyValueStore。确保数据存在。
  2. 删除了应用程序(假设数据仍然存在于iCloud中)。
  3. 等待几分钟以防万一,然后从Xcode内部安装并启动应用程序。
  4. 尝试使用[[NSUbiquitousKeyValueStore defaultStore] dataForKey:@“mykeyname”读取设置键 - 有时候没关系,但有时候找不到密钥!
  5. 等了15秒,又试了一次。成功。困惑。
  6. 所以似乎ios需要一些时间来为我的应用程序提供远程键值存储,以便在本地为dataForKey:call。 如果我写了这样一个系统(实际上我做过 - 前一段时间,在另一个生命中),在询问和接收键值数据之前显然必须有一个延迟。所以我想要一些通知说:“我们在第一次启动时完成了下载/同步键值存储”或类似的东西。

    据我所知,我可以在主线程中同步使用NSUbiquitousKeyValueStore(对我来说很方便)。但是[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]返回一个有效的url,然后我得到“key not found”。所以我不能依赖它。有没有办法确保下载NSUbiquitousKeyValueStore的工作原理?这一点非常重要,特别是在网速较慢的情况下。

    更新

    添加[[NSUbiquitousKeyValueStore defaultStore] synchronize](如在apple docs中所写)到init和load有点帮助。 iCloud还有很多问题。

    昨天我已成功将数据保存到手机1上的键值存储区并在手机2上恢复。 今天我删除了手机2上的应用程序并尝试恢复数据。但即使[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]返回有效的URL我调用[[NSUbiquitousKeyValueStore defaultStore] synchronize]我在调用dataForKey:MY_DATA_KEY时得到nil。

    当我尝试从手机1上的icloud恢复数据(应用程序仍然安装)时,它成功,但是当我在此手机上重新安装时,应用程序恢复不再成功。

    临时解决方案是:“关闭iCloud->文档和数据 - 关闭和打开网络 - 打开文档和数据”,但是你应该等几分钟,然后它应该工作。

    所以,问题: 你对iCloud有这样的问题吗? 2.有没有办法找出数据不可用或尚未下载? 3. iCloud有任何已知的“延迟”吗?我听说大约7秒钟,但显然不是这样。 4.似乎当应用程序没有未加载时,iCloud数据的更新速度非常快(秒),但是当您重新安装应用程序时,icloud需要几分钟才能实现键值存储。有没有办法强迫这个过程?


    P.S。 下面是我的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年。安卓已经成为ios的邪恶双胞胎,人类已经发现了引力波,希格斯已经获得了他的诺贝尔奖,微软已经收购并杀死了诺基亚,每个人都分别看到了安伯赫德和詹妮弗劳伦斯。但是iCloud仍然像它一样愚蠢。

    最后,我在几个VPS上制作了自己的网络服务堆栈。我拒绝使用第三方服务,因为大多数服务都不稳定且不可预测。然而我需要iCloud。因为另一个死于苹果的孩子不起作用。 SecKeyChain。当我的游戏开始时它的服务就会消失所以我决定将随机UUID存储在云中以区分用户(即使重新安装后也没有设备ID)。但可能出现什么问题?一切!我花了两天时间让这个愚蠢的部署没有错误,现在它不时丢失我的数据!

    谢谢Apple,谢谢,谢谢,谢谢! LA-LA-LA!太好啦! (马戏音乐的声音,渐渐变成哭泣)

3 个答案:

答案 0 :(得分:4)

<强>结论

临时解决方案是: - 从键值存储中获取数据之前调用synchronize - 确保它可以“关闭iCloud-&gt;文档和数据 - 关闭并再次在网络上 - 打开文档和数据”,但是你也应该等待几分钟,然后iCloud下载所有需要的数据

注意:安装应用程序并且已经工作(保存/加载)时,iCloud数据的键值存储更新速度非常快(7-15秒),但是当您重新安装应用程序时,似乎icloud需要几分钟才能完成实现键值存储。

我很高兴听到你的想法,因为icloud看起来几乎无法使用。但我不想设置自己的服务器来获得相同的功能。

答案 1 :(得分:1)

我正在为NSUbiquitousKeyValueStore设置一个虚拟密钥,并在应用启动时调用synchronize。结果不是100%解决方案,而是更好一些。你可以试试这个。

答案 2 :(得分:-1)

因为显然你的应用程序不应该在等待慢速网络时挂起。全部都在iCloud Design Guide

注册NSUbiquitousKeyValueStoreDidChangeExternallyNotification,调用-synchronize,并希望通知最终到达。

如果数据已经是最新的,我认为你没有得到通知,我认为没有一种方法可以知道数据的年龄。