3

我有一个已经投入生产一段时间的应用程序,但在 iOS 7 上,与应用程序交互一段时间后,转换有明显的延迟。我使用时间分析器来查看需要很长时间的事情,这cache_eraseImp_nolock似乎是罪魁祸首。(它花费的时间比objc_msgSend!)它似乎是在视图控制器转换发生时调用的。(推视图控制器,呈现模态视图控制器等)

有没有人看到类似的东西?我能做些什么来防止这个问题?

我正在尝试使用从头开始构建的应用程序来重现此问题,但到目前为止还没有运气。

仪器截图

4

3 回答 3

5

UIAppearance 在 iOS7.0.x 上非常缓慢。通过上面的堆栈跟踪,似乎应用外观方法每次(或至少多次)调用 method_exchangeImplementations(),这反过来又必须清除目标类和所有子类的 ObjC 运行时中的缓存,它会在执行此操作时保持全局锁定-非常慢。我不知道 UIAppearance 是否发生了变化,或者 ObjC 运行时发生了变化,这使得这种情况比以前慢得多。

此外,我想我已经看到 UILabel setShadowOffset: 和 setShadowColor: 方法也很慢,因为它们似乎是外观混合版本,因此有同样的问题(尽管它们没有被标记为 UI_APPEARANCE_SELECTOR)。

一种整体解决方案是尽可能不使用 UIAppearance 或根本不使用。对于 UILabel 方法,首先检查值是否需要更改,如果不需要,请不要调用 setter。

另一个可能的解决方案是CTAppearance,它是 UIAppearance 的重新实现,至少对于基于 UIView 的调用(它对 UIBarItem 外观调用没有任何作用,仍然很慢)。它目前仅在拉取请求中需要一些补丁,并且在没有一些帮助的情况下不会处理 UIPopover 容器。它的行为也将与 UIAppearance 有点不同(例如,它可能会根据调用时间覆盖某些显式设置的值),但在某些情况下它可能会有所帮助(很多)。

注意: UIAppearance 似乎在 iOS 7.1 中已修复。

于 2013-11-14T19:30:17.437 回答
5

iOS 7.0 已逐步淘汰 UIAppearance 并使其在某些情况下变得不稳定。检查您对基于 UIAppearance 的 API 的调用,并在必要时将其删除。*

iOS 7.1 似乎已经修复了很多之前的错误并加快了 UIAppearance 与运行时的交互。尽管如此,自 iOS 6 以来,UIAppearance 的语义已经发生了巨大变化。请谨慎行事。

*这只是在 iOS 7 测试版 SDK 推出时部分正确。

于 2013-10-02T20:41:50.857 回答
0

看来 Apple 已经在 iOS 7.1 中解决了这个问题,但是对于那些计划支持 iOS7 的人,我已经将一个相当简单的解决方法上传到 Github:SGVAppearanceProxy

这不是 UIAppearance 的重新实现,而只是一个旨在无缝包装原始-[UIView appearance].

该实现基于以下观察:只有具有多个参数的外观方法和具有轴值的方法才是导致广泛访问cache_eraseImp_nolock().

这基本上是原始代理的代理,它使用 Objective-C 的转发机制在外观方法发送到原始外观代理之前捕获外观方法的 NSInvocation,将唯一的新选择器添加到目标类,方法签名将 NSInvocation 作为单个参数,最后构造一个新的 NSInvocation 传递原始的作为参数。然后在原始代理上调用此调用。然后,自定义选择器的 IMP 只需调用原始调用,而无需原始 UIAppearance 逻辑执行任何昂贵的调配。

请注意,这仅适用于 UIView,因为非 UIView 启用 UIAppearance 的类(例如 UIBarButtonItem)有一个私有的内部视图类,所有外观调用都被路由到,因此似乎没有合法的方式向这些类添加自定义选择器类。

于 2014-05-29T13:54:56.770 回答