合理使用iCloud Key-Value存储

前言

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

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

使用场景

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

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。
欢迎关注我的公众号 沙拉可乐 ,分享独立开发的干货和背后的故事。

你可能感兴趣的:(合理使用iCloud Key-Value存储)