【问题标题】:Notification for property changes in NSObjectNSObject 中的属性更改通知
【发布时间】:2013-11-26 19:14:10
【问题描述】:

是否可以通知对象中所有属性的更改?每当更改 NSObject 中的属性之一时,我都希望调用 selector

到目前为止,我只看到了keyPathsForValuesAffectingValueForKey:,这不是我想要的,但它一直出现在我的搜索结果中。

我目前的具体目标是拥有一个“FilterModel”,它具有特定过滤器特性的属性。当任何属性发生变化时,我想用新过滤的结果更新UITableView

【问题讨论】:

  • @StephenJ 谢谢!不过,这实际上是最好的方法吗?看起来很老套。
  • 该答案中的解决方案确实很hacky,如果聪明的话。它的弱点之一是它只适用于声明的@properties。具有通常声明的访问器方法的“旧”样式属性,完全有效,并且会自动发出 KVO 通知,不会被覆盖。
  • @RileyE 请将您的解决方案作为答案发布,而不是作为问题的一部分。
  • @AaronBrager 好的。对于那个很抱歉。我不想放弃帮助我达到这一点的当前答案。

标签: ios properties nsnotificationcenter key-value-coding


【解决方案1】:

根据您的需要,这两个选项之一可能就是您正在寻找的。​​p>

KVO 观察

KVO Observing 提供了一个框架,用于自动监听属性更改,而无需修改 getter 和 setter。只需通过以下方式将您的监听对象添加为观察者或您的目标对象:

addObserver:forKeyPath:options:context:

然后在你的观察者的实现中,实现:

observeValueForKeyPath:ofObject:change:context:

并检查捕获通知的路径是否与您想要的属性匹配。有关接收 KVO 通知的更多信息,请参阅this。有关 KVO Observing 概念的更多详细信息和更好的可视化,请参阅KVO Developer documentation

手动 NSNotifications

如果您想微调您感兴趣的更改,每当您通过 setter 对感兴趣的对象进行更改时,都会通过以下方式发布通知:

postNotificationName:object:userInfo:

这将使您能够精确地自定义您感兴趣的更改,而不是希望从keyPathsForValuesAffectingValueForKey: 之类的东西中获得您正在寻找的确切行为。然后,您可以通过以下方式在相关视图中侦听您命名的通知来捕获这些通知:

addObserver:selector:name:object:

您可以在NSNotificationCenter documentation 上阅读有关发布通知的更多信息。

【讨论】:

  • 到那时,我不妨重写 setter 并执行更改。我希望有一种方法可以让模型在任何组件发生更改时执行触发器/通知,而不必覆盖每个单独的属性。有一个可以应用于所有类似情况的 iOS 解决方案会很方便。
  • 嗯,在这种情况下,您是否查看过KVO Observing?这可能更符合您的要求。
  • 这不是覆盖了 setter 和 getter 吗?
  • 属性的getter和setter?我不这么认为,您只需通过addObserver:forKeyPath:options:context: 将您的监听对象添加为观察者或您的目标对象。然后在您的观察者中实现observeValueForKeyPath 并检查捕获通知的路径是否与您想要的属性匹配。这里有更好的 KVO 观察可视化:developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…
  • 好的。我误解了你的意思,因为我是 KVC 的新手。
【解决方案2】:

您可以通过使用 Objective-C 运行时查询对象的属性列表并将您的自定义类注册为它们的观察者来做到这一点。

id yourObject;

objc_property_t* properties = class_copyPropertyList([yourObject class], &count);
NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
    const char* propertyName = property_getName(properties[i]);
    NSString *stringPropertyName = [NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
    [yourObject addObserver:self forKeyPath:stringPropertyName options:NSKeyValueObservingOptionNew context:nil];
}

另外,不要忘记在释放之前从观察者列表中删除您的自定义类。每当一个属性发生变化时,调用的方法是:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

【讨论】:

  • 哦!这看起来很有希望!我将使用稍微不同的实现对其进行测试。
  • 每当一个属性改变时,调用的方法就是 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context跨度>
  • 哇。这对于我正在寻找的东西来说实际上是惊人的和完美的。您可以将该方法添加到您的答案中吗?
  • KVO 听起来不错,但 I don't think you should use it.
  • 夸张,你打开了我的世界到另一个很棒的博客!谢谢你。而且你看起来很眼熟,要么你曾经主持过我,要么曾经帮助过我。
【解决方案3】:
- (id)init
{
    if(self = [super init])
    {
        unsigned int propertyCount;
        objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
        for(int i = 0; i < propertyCount; i++)[self addObserver:self forKeyPath:[NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding] options:NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}

- (void)dealloc
{
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
    for(int i = 0; i < propertyCount; i++)[self removeObserver:self forKeyPath:[NSString stringWithCString:property_getName(properties[i]) encoding:NSUTF8StringEncoding]];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"Changed value at key path: %@", keyPath);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-29
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    • 2017-05-21
    相关资源
    最近更新 更多