KVO用于整个对象属性

时间:2012-10-01 12:33:50

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

是否可以添加观察者以获取通知是否修改了任何受监视对象属性?例如:

@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被修改,则会收到通知。如果我注册了hold对象似乎不起作用(因为我修改了property1时没有真正修改myObject,所以这是有道理的。)

3 个答案:

答案 0 :(得分:13)

我可以想到两个选择。

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

    @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);
    }
    

答案 1 :(得分:2)

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

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

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

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

    myObject.name = name;

    [self didChangeValueForKey:@"myObject"];
}

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

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

:: EDIT :: 话虽如此,你应该可以使用......

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

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

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

答案 2 :(得分:1)

DrummerB答案的快速版本,带有删除观察结果的附加功能(例如,在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)
        }
    }
}