合理使用iCloud Key-Value存储

前言

最近在项目中使用了iCloud的Key-Value云存储,API本身非常简单,网上相关文章比较多,但是很多关键点没写出来。本文是我参考了官方文档Designing for Key-Value Data in iCloud结合实际开发情况,总结了一些使用过程要注意的地方

写代码前的配置过程这里不表,具体可以参考其他文章

使用场景

受限于存储空间,iCloud key-value仅适用于保存小数据,如用户偏好、系统设置和一些简单的App状态。如果需要存储大文件、数据库数据等,可以使用iCloud其他API:Using Document Storage with iCloudCloudKit

iCloud key-value存储限制:

  • 每个App每个用户总共最多可存储1MB数据
  • key的最大数量是1024
  • 每个key的最大长度是64Bytes
  • 每个key最大可以存储1MB的value

如果你使用了一个key存储了一个1MB的value,那么存储容量已经到达上限;如果你每个key的都存储1kb的数据,那么你可以使用1000个这样的key存储

特别注意NSData

虽然value可以是NSData数据类型,但是因为空间的关系,苹果并不推荐存储该类型的数据。另外,每次你对data只是做了很小的改动,在传输过程中,依然会把整个data重新上传。 推荐的做法是,在业务层面,将data尽可能分成多个key去存储,避免每次做多余的上传。

核心代码

增删改查

用法和NSUserDefaults一样,使用setValue:ForKey:方法存储,但是并不需要调用synchronize方法,调用了反而可能在特定情况下出错,具体参考这里Basic iCloud Key-Value storage test failing

NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];

//存储
[store setObject:@"testValue" forKey:@"testKey"];
 
//读取
[store objectForKey:@"testKey"];

//删除
[store removeObjectForKey:@"testKey"];

监听变化

苹果推荐在App启动的时候初始化监听,这样整个生命周期都可以获取到其他设备修改的值

And at launch time, you should call the synchronize method manually to detect if any changes were made externally. You do not need to call that method at other times during you app’s execution

并且在启动时候应该调用一次synchronize方法,除此外,整个生命周期中,你不再需要再调用此方法。
以下代码在App启动时执行:

//监听变化
NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
          selector:@selector(updateKVStoreItems:)
          name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
          object:store];
          
//同步数据,注意这个代码顺序不能颠倒,一定要先监听,后同步
[store synchronize];
//通知方法
- (void)updateKVStoreItems:(NSNotification*)notification {
   // 获取变化对应的Key
   NSDictionary* userInfo = [notification userInfo];
   NSNumber* reasonForChange = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey];
   NSInteger reason = -1;
 
   if (!reasonForChange)
      return;
 
   /*
   * 获取变化
   * NSUbiquitousKeyValueStoreServerChange - 服务器变化
   * NSUbiquitousKeyValueStoreInitialSyncChange - 初始化同步
   */
   reason = [reasonForChange integerValue];
   if ((reason == NSUbiquitousKeyValueStoreServerChange) ||
         (reason == NSUbiquitousKeyValueStoreInitialSyncChange)) {
      // If something is changing externally, get the changes
      // 更新本地数据
      NSArray* changedKeys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
      NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
 
      // 以下代码,仅适用于本地和服务器使用一样的Key,否则自行处理
      for (NSString* key in changedKeys) {
         id value = [store objectForKey:key];
         [userDefaults setObject:value forKey:key];
      }
   }
}

测试

我是用了两部手机,登录了同一个AppleID。模拟器没试过,这里就不说了,应该也是可以的。

在测试过程中发现一个问题,获取值偶尔会失败,在网上搜索相关问题,最终在苹果文档找到答案:

The key-value store is intended for storing data that changes infrequently. If the apps on a device make frequent changes to the key-value store, the system may defer the synchronization of some changes in order to minimize the number of round trips to the server. The more frequently apps make changes, the more likely it is that later changes will be deferred and not show up on other devices right away

大意是:iCloud Key-Value应该用于存储那些不经常修改的数据,如果App频繁修改数据,系统会自动延迟同步时间,因为它会尽可能减少和服务器交互的次数。 并且越频繁,延迟的可能性会越大。

因为我测试的时候频繁删除和存储,导致我存储其实还没上传到服务器,此时删除App,重新安装,当然获取不到值。 知道苹果延迟的这个机制后,我在存储后特地等了几分钟再删除App,重新安装后又是过几分钟后,终于能正常获取我存储的值。

以上就是所有内容,本身比较简单,只要注意我提到的关键点就OK。

参考

Storing Preferences in iCloud
NSUbiquitousKeyValueStore
Designing for Key-Value Data in iCloud

最后

我是一名在职iOS开发工程师,业余时间独立开发App。
欢迎关注我的公众号 沙拉可乐 ,分享独立开发的干货和背后的故事。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容