18

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

我有一个奇怪的问题。脚步:

  1. 安装了一个应用程序并将其数据保存到 NSUbiquitousKeyValueStore。确保数据在那里。
  2. 删除了应用程序(假设数据仍保留在 iCloud 中)。
  3. 等了几分钟以防万一,然后从 Xcode 内部安装并启动了该应用程序。
  4. 尝试使用 [[NSUbiquitousKeyValueStore defaultStore] dataForKey: @"mykeyname"] 读取设置键 - 有时可以,但有时找不到键!
  5. 等了15秒,再试一次。成功。使困惑。

因此,似乎 ios 需要一些时间来使我的应用程序的远程键值存储在本地可用于 dataForKey: 调用。如果我写了这样一个系统(实际上我写了 - 前一段时间,在另一种生活中),那么在询问和接收键值数据之前显然必须有一个延迟。所以我想要一些通知说:“我们在第一次启动时完成了下载/同步键值存储”或类似的东西。

据我了解,我可以在主线程中同步使用 NSUbiquitousKeyValueStore(这对我来说很方便)。但是 [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] 返回一个有效的 url,然后我得到“找不到密钥”。所以我不能依赖它。有没有办法确保 NSUbiquitousKeyValueStore 工作并被下载?这很重要,尤其是在互联网速度较慢的情况下。

更新

将 [[NSUbiquitousKeyValueStore defaultStore] synchronize] (如苹果文档中所写)添加到 init 和 load 有点帮助。iCloud仍有许多问题。

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

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

临时解决方案是:“关闭 iCloud->Documents&Data - 关闭并打开网络 - 打开 Documents&Data”,但你应该等待几分钟,然后它应该可以工作。

所以,问题:

  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 的邪恶双胞胎,人类发现了引力波,希格斯获得了诺贝尔奖,微软收购并杀死了诺基亚。但 iCloud 仍然像以前一样愚蠢。

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

谢谢苹果,谢谢,谢谢,谢谢!啦啦啦!太好啦!(马戏团的音乐声,逐渐变成哭泣声)

4

3 回答 3

4

结论

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

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

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

于 2012-09-22T00:05:44.760 回答
1

我正在为 NSUbiquitousKeyValueStore 设置一个虚拟键并在应用启动时调用同步。结果不是 100% 的解决方案,而是稍微好一些。你可以试试这个。

于 2013-02-28T06:09:10.247 回答
-1

因为显然您的应用程序不应该在等待慢速网络时挂起。这一切都在iCloud 设计指南中。

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

如果数据已经是最新的,我认为您不会收到通知,而且我认为没有一种方法可以知道数据的历史。

于 2012-09-22T22:36:55.227 回答