【问题标题】:KVO for whole object properties整个对象属性的 KVO
【发布时间】:2012-09-22 07:22:03
【问题描述】:

如果任何被监控的对象属性被修改,是否可以添加一个观察者来获得通知?例如:

@interface OtherObject : NSObject

@property (nonatomic) MyObject* myObject;

@end

@interface MyObject : NSObject

@property (nonatomic) unsigned int property1;
@property (nonatomic) unsigned int property2;

@end

我想做这样的事情:

[otherObject addObserver:self
               forKeyPath:@"myObject"
                  options:0
                  context:nil]

并在 property1 或 property2 被修改时得到通知。如果我注册持有对象,它似乎不起作用(不知何故,因为 myObject 在我修改 property1 时并没有真正修改)。

【问题讨论】:

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


    【解决方案1】:

    我能想到两个选择。

    • 您可以创建一个单独的“主”属性并使其依赖于您的所有其他属性。

      @interface MyObject : NSObject
      @property (nonatomic) id masterProperty;
      @property (nonatomic) unsigned int property1;
      @property (nonatomic) unsigned int property2;
      @end
      

      + (NSSet *)keyPathsForValuesAffectingMasterProperty {
          return [NSSet setWithObjects:@"property1", @"property2", nil];
      }
      

      如果您观察到masterProperty,您将在任何属性发生更改时收到通知。

    • 您使用 Objective-C 运行时获取所有属性的列表并观察它们。

      - (void)addObserverForAllProperties:(NSObject *)observer
                                  options:(NSKeyValueObservingOptions)options
                                  context:(void *)context {
          unsigned int count;
          objc_property_t *properties = class_copyPropertyList([self class], &count);
          for (size_t i = 0; i < count; ++i) {
              NSString *key = [NSString stringWithCString:property_getName(properties[i])];
              [self addObserver:observer forKeyPath:key 
                        options:options context:context];
          }
          free(properties);
      }
      

    【讨论】:

    • 大概,您还可以结合这两种方法,使用 Objective-C 运行时获取 keyPathsForValuesAffectingMasterProperty 中所有属性的列表,然后在 NSSet 中返回它们。 (使用静态变量可能是个好主意,这样您只需执行一次。)
    • 在下面添加了 Swift 版本:stackoverflow.com/a/59839327/1176162
    【解决方案2】:

    你可以做的是有一个函数来修改 myObject 的特定属性...

    -(void)setMyObjectName:(NSString*)name;
    

    然后在函数中有这段代码...

    - (void)setMyObjectName:(NSString*)name
    {
        [self willChangeValueForKey:@"myObject"];
    
        myObject.name = name;
    
        [self didChangeValueForKey:@"myObject"];
    }
    

    这将在 myObject 上的该属性发生更改时通知观察者。

    如果您需要这样做,请使用此模式,您可以收到有关 myObject 任何更改的通知。

    ::编辑:: 话虽如此,您应该可以使用...

    [otherObject addObserver:self
                  forKeyPath:@"myObject.property1"
                     options:0
                     context:nil];
    

    这将观察 property1 并对其他属性执行相同的操作。

    但这意味着为每个属性单独添加一个观察者。

    【讨论】:

    • 也许 willChangeValueForKey 而不是 willChangeObjectForKey?
    • @Andy 改变了。很惊讶两年都没有被发现。大声笑!
    【解决方案3】:

    DrummerB 答案的 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)
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-10
      • 1970-01-01
      • 2014-07-16
      • 2016-03-30
      • 1970-01-01
      • 1970-01-01
      • 2011-08-07
      • 2021-03-28
      • 1970-01-01
      相关资源
      最近更新 更多