【发布时间】:2017-04-30 15:01:31
【问题描述】:
在我的应用程序中,我想将每个用户登录的用户设置保存在 plist 文件中,我写了one class called CCUserSettings,它与NSUserDefaults 具有几乎相同的界面,它读取和写入与当前用户相关的 plist 文件ID。它可以工作,但性能很差。每次用户调用[[CCUserSettings sharedUserSettings] synchronize] 时,我都会将NSMutableDictionary(保留用户设置)写入plist 文件,下面的代码显示synchronize 的CCUserSettings 省略了一些琐碎的细节。
- (BOOL)synchronize {
BOOL r = [_settings writeToFile:_filePath atomically:YES];
return r;
}
我想NSUserDefaults应该在我们调用[[NSUserDefaults standardUserDefaults] synchronize]时写入文件,但是它运行得非常快,我写了一个demo来测试,关键部分如下,运行1000次[[NSUserDefaults standardUserDefaults] synchronize]和[[CCUserSettings sharedUserSettings] synchronize]在我的 iPhone6 上,结果是 0.45 秒 vs 9.16 秒。
NSDate *begin = [NSDate date];
for (NSInteger i = 0; i < 1000; ++i) {
[[NSUserDefaults standardUserDefaults] setBool:(i%2==1) forKey:@"key"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
NSDate *end = [NSDate date];
NSLog(@"synchronize seconds:%f", [end timeIntervalSinceDate:begin]);
[[CCUserSettings sharedUserSettings] loadUserSettingsWithUserId:@"1000"];
NSDate *begin = [NSDate date];
for (NSInteger i = 0; i < 1000; ++i) {
[[CCUserSettings sharedUserSettings] setBool:(i%2==1) forKey:@"_boolKey"];
[[CCUserSettings sharedUserSettings] synchronize];
}
NSDate *end = [NSDate date];
NSLog(@"CCUserSettings modified synchronize seconds:%f", [end timeIntervalSinceDate:begin]);
结果显示,NSUserDefaults 比我的CCUserSettings 快了近 20 倍。现在我开始怀疑“NSUserDefaults 真的每次我们调用synchronize 方法时都会写入plist 文件吗?”,但如果没有,它如何保证在进程退出之前将数据写回文件(作为进程可能随时被杀死)?
这几天我想出了一个想法来改进我的CCUserSettings,它是mmapMemory-mapped I/O。我可以将虚拟内存映射到文件,并且每次用户调用synchronize 时,我都会使用NSPropertyListSerialization dataWithPropertyList:format:options:error: 方法创建NSData 并将数据复制到该内存中,操作系统将在进程退出时将内存写回文件。但是由于文件大小不固定,我可能得不到很好的性能,每次数据长度增加,我都要重新mmap一个虚拟内存,我相信这个操作很耗时。
抱歉我的冗余细节,我只想知道NSUserDefaults 是如何实现如此好的性能的,或者任何人都可以有一些好的建议来改进我的CCUserSettings 吗?
【问题讨论】:
-
这是一篇关于
NSUserDefaults的好文章:dscoder.com/defaults.html。它的作者是苹果公司的一名工程师,所以可以肯定地假设他知道他在说什么:) -
@Losiowaty 感谢您的链接,但我认为它谈到了 mac ox 中的实现,因为它说“设置一个值将(最终,它是异步的,并且稍后在另一个进程中发生)写将整个 plist 输出到磁盘,无论变化多么小。”。如果你修改 NSUserDefaults 并在没有
synchronize的情况下杀死你的应用程序,设置将不会写入文件,所以我认为在 iOS 中没有另一个进程写入文件。 -
如果您修改 NSUserDefaults 并终止应用程序,您必须在终止按钮上非常 快才能丢失数据。几毫秒左右。这在 iOS 8 中发生了变化;在此之前,以这种方式丢失数据要容易得多。
标签: ios nsuserdefaults mmap virtual-memory