【问题标题】:Key Value Observing - how to observe all the properties of an object?Key Value Observing - 如何观察一个对象的所有属性?
【发布时间】:2026-02-03 04:55:01
【问题描述】:

我对使用键值观察 (KVO) 以及如何注册以接收属性更改通知感到满意:

[account addObserver:inspector
          forKeyPath:@"openingBalance"
             options:NSKeyValueObservingOptionNew
              context:NULL];

但是,如果我想观察帐户对象的所有属性的变化,我该如何实现呢?我必须为每家酒店注册通知吗?

【问题讨论】:

    标签: objective-c key-value-observing


    【解决方案1】:

    按照 david van brink 的建议,以下 Swift 代码为每个属性添加了观察结果。它有一个额外的功能来删除观察(例如在deinit):

    extension NSObject {
        func addObserverForAllProperties(
            observer: NSObject,
            options: NSKeyValueObservingOptions = [],
            context: UnsafeMutableRawPointer? = nil
        ) {
            performForAllKeyPaths { keyPath in
                addObserver(observer, forKeyPath: keyPath, options: options, context: context)
            }
        }
    
        func removeObserverForAllProperties(
            observer: NSObject,
            context: UnsafeMutableRawPointer? = nil
        ) {
            performForAllKeyPaths { keyPath in
                removeObserver(observer, forKeyPath: keyPath, context: context)
            }
        }
    
        func performForAllKeyPaths(_ action: (String) -> Void) {
            var count: UInt32 = 0
            guard let properties = class_copyPropertyList(object_getClass(self), &count) else { return }
            defer { free(properties) }
            for i in 0 ..< Int(count) {
                let keyPath = String(cString: property_getName(properties[i]))
                action(keyPath)
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      似乎没有内置函数可以订阅对象所有属性的更改。

      如果您不关心究竟是哪个属性发生了变化并且可以更改您的类,您可以向其添加虚拟属性以观察其他属性的变化(使用+ keyPathsForValuesAffectingValueForKey+keyPathsForValuesAffecting&lt;Key&gt; 方法):

      // .h. We don't care about the value of this property, it will be used only for KVO forwarding
      @property (nonatomic) int dummy;
      
      #import <objc/runtime.h>
      //.m
      + (NSSet*) keyPathsForValuesAffectingDummy{
      
          NSMutableSet *result = [NSMutableSet set];
      
          unsigned int count;
          objc_property_t *props = class_copyPropertyList([self class], &count);
      
          for (int i = 0; i < count; ++i){
              const char *propName = property_getName(props[i]);
              // Make sure "dummy" property does not affect itself
              if (strcmp(propName, "dummy"))
                  [result addObject:[NSString stringWithUTF8String:propName]];
          }
      
          free(props);
          return result;
      }
      

      现在,如果您观察 dummy 属性,每次更改任何对象的属性时都会收到 KVO 通知。

      您还可以像发布的代码一样获取对象中所有属性的列表,并在循环中为每个属性订阅 KVO 通知(因此您不必对属性值进行硬编码) - 这样您就可以如果需要,请更改属性名称。

      【讨论】:

      • 这似乎是间接的,但为我指明了正确的方向。 class_copyPropertyList() 和 property_getname() 足以添加对每个属性的观察,完全按照最初的要求。
      • 那么上面的sn-p代码有更新吗?