菜鸟教程小白 发表于 2022-12-13 14:28:28

ios - `NSUserDefaults synchronize` 怎么跑得这么快?


                                            <p><p>在我的应用程序中,我想为每个登录的用户将用户设置保存在 plist 文件中,我写了 <a href="https://github.com/kudocc/CCKit/tree/master/CCKitDemo/other/NSUserDefaults" rel="noreferrer noopener nofollow">one class called <code>CCUserSettings</code></a>它具有与 <code>NSUserDefaults</code> 几乎相同的接口(interface),它读取和写入与当前用户 ID 相关的 plist 文件。它可以工作,但性能很差。每次用户调用 <code>[ synchronize]</code> 时,我都会将 <code>NSMutableDictionary</code>(保留用户设置)写入 plist 文件,下面的代码显示 <code>synchronize <code>CCUserSettings</code> 的 </code> 省略了一些琐碎的细节。 </p>

<pre><code>- (BOOL)synchronize {
    BOOL r = ;
    return r;
}
</code></pre>

<p>我想 <code>NSUserDefaults</code> 应该在我们调用 <code>[ synchronize]</code> 时写入文件,但是它运行得非常快,我写了一个 <a href="https://github.com/kudocc/CCKit/blob/master/CCKitDemo/performance/store/PerformanceStoreViewController.m" rel="noreferrer noopener nofollow">demo</a>进行测试,关键部分如下,在我的 iPhone6 上运行 1000 次 <code>[ synchronize]</code> 和 <code>[ synchronize]</code>,结果是 0.45 秒与 9.16 秒相比。</p>

<pre><code>NSDate *begin = ;
for (NSInteger i = 0; i &lt; 1000; ++i) {
    [ setBool:(i%2==1) forKey:@&#34;key&#34;];
    [ synchronize];
}
NSDate *end = ;
NSLog(@&#34;synchronize seconds:%f&#34;, );


[ loadUserSettingsWithUserId:@&#34;1000&#34;];
NSDate *begin = ;
for (NSInteger i = 0; i &lt; 1000; ++i) {
    [ setBool:(i%2==1) forKey:@&#34;_boolKey&#34;];
    [ synchronize];
}
NSDate *end = ;
NSLog(@&#34;CCUserSettings modified synchronize seconds:%f&#34;, );
</code></pre>

<p>结果显示,<code>NSUserDefaults</code> 比我的 <code>CCUserSettings</code> 快了近 20 倍。现在我开始怀疑“NSUserDefaults 真的每次我们调用 <code>synchronize</code> 方法时都会写入 plist 文件吗?”,但如果没有,它如何保证数据在进程退出(因为进程可能随时被杀死)?</p>

<p>这几天我想出了一个改进我的<code>CCUserSettings</code>的想法,它是<code>mmap</code> <a href="http://www.gnu.org/software/libc/manual/html_node/Memory_002dmapped-I_002fO.html" rel="noreferrer noopener nofollow">Memory-mapped I/O</a> .我可以将虚拟内存映射到文件,并且每次用户调用 <code>synchronize</code> 时,我都会使用 <code>NSPropertyListSerialization dataWithPropertyList:format:options:error:</code> 创建一个 <code>NSData</code> > 方法并将数据复制到该内存中,操作系统将在进程退出时将内存写回文件。但是我可能得不到好的性能,因为文件大小不固定,每次数据长度增加,我都要重新<code>mmap</code>一个虚拟内存,我相信这个操作很耗时。</p>

<p>抱歉我的冗余细节,我只是想知道 <code>NSUserDefaults</code> 是如何实现如此好的性能的,或者任何人都可以有一些好的建议来改进我的 <code>CCUserSettings</code> 吗?</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>在现代操作系统(iOS 8+、macOS 10.10+)上,NSUserDefaults 在您调用同步时不会写入文件。当您调用 -set* 方法时,它会向名为 cfprefsd 的进程发送一条异步消息,该进程存储新值、发送回复,然后稍后将文件写出。所有 -synchronize 所做的是等待所有未完成的消息到 cfprefsd 接收回复。</p>

<p>(编辑:如果愿意,您可以通过在 xpc_connection_send_message_with_reply 上设置符号断点然后设置用户默认值来验证这一点)</p></p>
                                   
                                                <p style="font-size: 20px;">关于ios -`NSUserDefaults synchronize` 怎么跑得这么快?,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/41156879/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/41156879/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - `NSUserDefaults synchronize` 怎么跑得这么快?