EK EventKit 的内存管理和性能

Posted

技术标签:

【中文标题】EK EventKit 的内存管理和性能【英文标题】:Memory Management & Performance with EK EventKit 【发布时间】:2014-05-24 17:27:23 【问题描述】:

在日历应用程序中,我根据 EventKit API 显示事件。我从EKEventStore 获取事件并以每日、每周、每月视图、列表等形式显示它们。现在我在 iPhone 4 上遇到了一些性能问题。

性能问题主要与速度有关。例如,折叠或展开所有表格视图部分(表示日期)以显示行(表示事件)需要几秒钟。为编辑/导出界面重新加载表格也需要 5-8 秒。我将不得不检查 Instruments 以提供更多详细信息。

到目前为止还没有内存问题。

我现在的策略是尽量减少内存占用。我在内存中使用数组,但它们只包含eventIdentifier,一个短字符串。我可以使用EKEventStore 方法eventWithEventIdentifier: 检索事件。我怀疑这是性能下降的原因。

我想到了两种选择:

    使用 EKEvent 对象而不是标识符。但是,我相信这对于内存来说是不可预测的。有些事件有很多文本,因此要保存在内存中的数据量不受限制。必须显示事件的时段的持续时间也可能非常长。

    将所有内容移植到 Core Data,可能会将原始 EKEvent 对象存储为可转换对象。这将是一个重大的重构,但我可以利用NSFetchedResultsController 及其优化功能。

我已经尝试了 1 2 - 性能仍然很差!

你的经验是什么?您是否看到重复调用EKEventStore 数据库时出现性能问题?你有什么建议?

更新:

Instruments 报告确实 tableView 的reloadData 需要很长时间(1.5 秒)。我不知道为什么,因为表格视图的状态(是否折叠部分)和整个数据都是在之前加载的,并且该代码是有效的。

计算任何单元格高度(据报道,有时这会在显示之前强制加载整个表格)。我打电话时出现同样的滞后

[tableView beginUpdates];
[tableView endUpdates];

为了动画部分的折叠。

注意:也许这个问题的主题应该改变,去掉 EventKit 部分。

【问题讨论】:

无论你做什么,都不要移植到 Core Data。提供的 API 在双向同步方面很糟糕。您将无法有效地听到偶数商店中的更改以更新您的 CD。 不正确。这是完美的工作。我只是遇到性能问题。 根据我的经验,最好是在后台线程上从事件存储中执行获取,一旦数据准备好,返回 UI 线程。 您如何聆听变化?如果你杀死了应用程序,你怎么知道哪些事件同时发生了变化? 您已将问题更改为说性能问题与表视图行状态的更改有关。你没有显示代码,也没有证据证明你已经测量过任何东西。您应该使用 Instruments 来衡量并找出需要时间的东西。如果修改 tableView 并更改所有行的状态,您可能做错了。担心您看不到的行的状态是没有意义的,对于适合屏幕上所有行的表格,您应该没有问题。您的展开/折叠状态不应影响 UI 性能。它可能应该被移动到模型层,所以你可以根据需要延迟加载行。 【参考方案1】:

虽然我从来没有用过EventKit,但我可以给你一些建议:

    NSFetchedResultsController 是一件很棒的事情,尤其是在最初加载表格视图后数据将发生变化时。 NSFetchedResultsController 监视数据的更改,NSFetchedResultsControllerDelegate 协议允许对 UITableView 进行增量更改,而不是在对填充表的数据进行更改时重新加载整个 UITableView。一般来说,尽可能避免在 UITableView 上使用完整的reloadData

    如果绘制 UITableView 真的很慢,很可能问题的根源在于 tableView:cellForRowAtIndexPath: 方法(几乎可以保证,尽管其他罪魁祸首可能是其他 UITableViewDataSource 方法,例如tableView:titleForHeaderInSection:tableView:titleForFooterInSection:)。如果您正在从磁盘读取,不使用缓存的 UITableViewCells,或者在这些方法中进行大量绘图,则可能会导致您的 UITableView 重新加载/滚动非常慢。

    1234563在 NSArray 中。然后你的 UITableViewDataSource 方法可以从 NSArray 而不是磁盘读取。

【讨论】:

1.我知道。我对FRC很熟悉。但是,它不适用于 FRC。 2. 这不是场景,即没有绘图,没有提取,正确回收等。 3. 在我原来的设置中这样做。 "一般来说,尽可能避免在 UITableView 上执行完整的 reloadData。"该规则的例外是当您一次更改托管对象中的 很多 个项目时,例如在大型导入期间。在这种情况下,reloadData 几乎总是比单个单元格动画更有效。【参考方案2】:

问题在于您的行动画和(可能)重新计算部分。这很常见,是 UITableView 的东西,而不是 EventKit 或 CoreData 的东西。更多分析应该让您了解如何优化您的部分以提高性能。

查看涉及部分的 UITableViewDataSource 方法的实现:

- numberOfSectionsInTableView:
- tableView:numberOfRowsInSection:
– tableView:sectionForSectionIndexTitle:atIndex:
– tableView:titleForHeaderInSection:
– tableView:titleForFooterInSection:

你可能在用那些比较繁重的方法做事。也许要构建您的部分,您必须查看所有数据(这很常见)。如果是这种情况,请想出一个合理的策略来缓存节信息并在事件存储更改时使其无效。

【讨论】:

不应该一个获取的结果控制器修复任何“查看所有数据以构建你的部分”的问题吗?这没有帮助。 不。获取的结果控制器死了并不能解决这个问题,而且会使事情变得更糟。它仍然需要查看所有数据来构建节,并且每次任何对象更改节键路径值时,它都可能重建所有节。因此,如果您要添加许多对象,则每次都会重建一个完整的部分。在不了解您的应用程序的情况下,我无法提出任何更具体的建议。例如,如果您的数据始终按特定顺序排列,您可以实现分页 - 这不适用于所有应用程序。更改部分的构建方式取决于您。 你写的没有意义。数据几乎没有变化,因此 FRC 应该注意优化其获取等,无论它是退休的部分还是行信息。 这个问题与获取无关。它与构建部分有关。要构建这些部分,获取的结果控制器需要访问与获取请求匹配的所有对象的部分键路径属性值。每次数据更改时都必须这样做——这就是为什么使用获取限制、批量大小等在使用部分时没有太大影响的原因——它仍然必须获取所有数据,并在每个对象上触发故障. 这很令人沮丧,因为您没有阅读我的 cmets。我声明数据不会改变。从来没有fetchLimit的问题。而且,不,FRC不必在计算部分时必须获取“所有数据”。

以上是关于EK EventKit 的内存管理和性能的主要内容,如果未能解决你的问题,请参考以下文章

C++应用程序性能优化——操作系统的内存管理

内存管理参考

SQL Server性能调优:资源管理之内存管理篇(上)

移动游戏加载性能和内存管理全解析

JavaScript 中的性能内存管理

从原理分析PHP性能