【问题标题】:Disable Key-Value Observing in Objective C在 Objective C 中禁用键值观察
【发布时间】:2013-04-05 10:11:14
【问题描述】:

我不使用 KVO,主要是出于性能原因,所以我想确保正确禁用它。

来自 Apple Key-Value Observing Programming Guide

自动支持(针对 KVO)由 NSObject 提供,默认情况下可用于符合键值编码的类的所有属性。通常,如果您遵循标准 Cocoa 编码和命名约定,则可以使用自动更改通知——您无需编写任何额外代码。

这是否意味着 Xcode 生成的每个属性都实现了 willChangeValueForKeydidChangeValueForKey 方法?

如果是这样,有什么方法(一些标志或其他东西)可以禁用这种行为吗? 我正在使用 accessInstanceVariablesDirectly 并且总是返回 NO,但我不确定这是否足够好。

【问题讨论】:

  • 您是否进行了分析以确定这是一个问题?如果不是,那么你的问题毫无意义。
  • 我如何分析它?将正常的财产使用情况与究竟是什么进行比较?也许对你来说这毫无意义,但我想知道我使用的编程语言到底是如何工作的。
  • 如果 KVO 花费太多时间,仪器将在调用您的属性时显示高 CPU 周期使用率(因为 KVO 调用是同步执行的)。说出于任何其他原因出于性能原因要禁用 KVO 是过早的优化。

标签: ios objective-c performance key-value-observing


【解决方案1】:

我不使用 KVO。

你无法真正知道这一点。 Cocoa 框架类或其他外部代码可能在您不知情的情况下依赖于 KVO。

此外,NSObject 的 KVO 使用一种称为 isa-swizzling 的技术,通过动态地对观察到的对象进行子类化来自动增强观察到的对象。这意味着没有观察到的对象没有开销。

您的其他问题:

accessInstanceVariablesDirectly 仅在 KVC 中使用,不连接到 KVO。覆盖它没有性能优势。

这是否意味着 Xcode 生成的每个属性都有 willChangeValueForKeydidChangeValueForKey 方法 实施了吗?

是的,每个对象都会响应这些方法。但是这些方法的存在并不意味着性能会受到任何影响。通常甚至不调用这些方法。

【讨论】:

    【解决方案2】:

    简而言之:

    通常,您在从外部访问对象时使用属性。然后,性能不应该是至关重要的,因为您可能没有执行繁重的工作。如果 KVO 是一个问题,您可能需要检查您的应用程序的设计。 从对象本身来说,最好直接访问ivars,这种情况下KVO是无关紧要的。

    更多详情:

    最近在苹果邮件列表中讨论了这个话题:
    When to use properties vs. ivars

    特别是 Jens Alfke(前 Apple 员工):

    • 作为类公共 API 的一部分。
    • 作为类实现中的抽象
    • 作为更改实例变量的便捷瓶颈 - 例如,如果您想在每次更改时触发其他内容 变量,将其设置为属性而不是编写很有用 直接到 ivar。
    • 在非 ARC 代码中,当您分配新值时,它可能是一种管理保留/释放舞蹈的便捷方式(但此问题 远离 ARC。)

    我见过的代码只是声明和使用所有的属性 内部状态——我认为这是个坏主意。真的很浪费 CPU 时间和代码大小,它不再为你买任何东西 现在我们有了 ARC。

    另外,请注意,除了 KVO 之外,还有其他性能问题。 John McCall(现任 Apple 员工)表示:

    属性以多种方式影响性能:

    1. 如前所述,发送消息进行加载/存储会更慢 而不仅仅是内联加载/存储。
    2. 发送消息进行加载/存储也是需要保存在 i-cache 中的更多代码:即使添加了 getter/setter 除了加载/存储之外的零额外指令,会有一个坚实的 调用者中有六个额外的指令来设置消息发送 并处理结果。
    3. 发送消息会强制将该选择器的条目保留在方法缓存中,并且该内存通常保留在 d-cache 中。 这会增加启动时间,增加您的静态内存使用量 应用程序,并使上下文切换更加痛苦。由于方法缓存 特定于对象的动态类,这个问题增加了 你在上面使用 KVO 的次数越多。
    4. 发送消息会强制函数中的所有值溢出到堆栈中(或保存在被调用者保存寄存器中,这意味着 在不同的时间溢出)。
    5. 发送消息可能会产生任意副作用,因此会强制编译器重置所有关于非本地的假设 记忆。
    6. 消息发送可能具有任意副作用,因此无法提升、沉没、重新排序、合并或消除。
    7. 在 ARC 中,消息发送的结果将始终由被调用者或调用者保留,即使返回 +0:即使 方法不保留/自动释放其结果,调用者不知道 并且必须尝试采取行动以防止结果得到 自动发布。这永远无法消除,因为消息发送是 不可静态分析。
    8. 在 ARC 中,由于 setter 方法通常将其参数设为 +0,因此无法将该对象的保留(如上所述,ARC 通常具有)“转移”到 ivar,因此值 通常需要保留/释放两次。

    当然,这并不意味着它们总是坏的——有一个 使用属性的很多充分理由。请记住,就像 许多其他语言功能,它们不是免费的。

    除了 KVO,在设计应用程序时还要考虑这些。

    【讨论】:

    • 是的,我对此很熟悉。即使使用普通 ivar 也比直接访问属性 ivar 更快。
    • 因此,与 KVO 相关的性能考虑是无关紧要的。在设计 KVO 之前,专家们已经考虑了这一方面。
    • @Jean 这一切都是真的,但它讨论了一个不同的主题:使用 ivars 与属性。最初的问题是关于 KVO 的存在是否会以某种方式影响性能。
    • 好的,但在某些情况下我必须使用 self.propertyName 访问属性。例如,在使用它的超类私有财产的子类中(在这种情况下必须是财产)。我可以在头文件中公开它,但我的代码被其他程序员使用,我不希望他们看到它。
    • OP 出于性能问题询问如何禁用 KVO。 这是一个非常不寻常的举动我的观点是:KVO 不应该是性能问题,因为应该使用属性和 ivars。如果 KVO 仍然是性能问题,要么需要重新考虑设计,要么存在与使用属性相关的其他瓶颈问题。因此,我发现在这里提及它们是合适的。即使这对 OP 没有帮助,也许它可以帮助其他人。 @H2CO3 我不介意,但我希望反对者能发表评论。
    【解决方案3】:

    这是否意味着 Xcode 生成的每个属性都实现了 willChangeValueForKey 和 didChangeValueForKey 方法?

    没有。除了这些属性不是由 Xcode 而是由编译器生成的事实之外,这意味着传统命名的属性在默认情况下是自动可观察的,并且您可以实现 KVO 方法 如果您需要它们. 并不是说​​每次设置属性时,都会调用跨类的各种方法。只有您指定的那些。

    所以,基本上,不用担心这个。仅 KVO 不会对性能产生影响。

    【讨论】:

      【解决方案4】:

      我认为这不会以任何方式影响性能。因此,如果您不想使用 KVO,可以忽略它。

      只有当您使用addObserver 观察 KV 变化时,观察才会开始并因此使用资源。

      【讨论】:

        【解决方案5】:

        你可以看看+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key。它是一个类方法,而不是实例,它将配置一个类型的所有对象。

        除了您的要求之外,这在您需要手动 KVO 而不是自动 KVO 时很有用。

        https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/#//apple_ref/occ/clm/NSObject/automaticallyNotifiesObserversForKey:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-04-04
          • 1970-01-01
          • 2011-03-29
          • 2012-08-25
          • 2010-11-26
          • 2011-12-05
          • 1970-01-01
          相关资源
          最近更新 更多