11

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

@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 时并没有真正修改)。

4

3 回答 3

15

我可以想到两个选择。

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

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

     </p>

    + (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);
    }
    
于 2012-10-01T12:55:17.190 回答
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 并对其他属性执行相同的操作。

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

于 2012-10-01T12:41:02.527 回答
1

DrummerB 答案的 Swift 版本,具有删除观察结果的附加功能(例如 in 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)
        }
    }
}
于 2020-01-21T10:44:42.320 回答