使用自定义属性getter的KVO通知

11

我正在尝试监听iOS上UITextVieweditable属性。在UITextView.h的头文件中,editable属性被定义为:

@property(nonatomic,getter=isEditable) BOOL editable;

我使用添加观察者模式来监听KVO通知,其中我将keyPath作为NSStringFromSelector(@selector(isEditable))进行传递,这样Xcode会在我使用未定义选择器时发出警告。注册isEditable键路径时没有问题,但是在更改文本视图上的editable属性后,我从未收到属性更改的通知。我使用以下代码注册观察者:

[self.textView addObserver:self
                forKeyPath:NSStringFromSelector(@selector(isEditable))
                   options:NSKeyValueObservingOptionNew
                   context:KVOTestingTestsContext];

然而,如果我使用keypath NSStringFromSelector(@selector(editable)),我会得到KVO通知,但是Xcode会生成警告,说我正在使用一个未声明的选择器“editable”。

我想知道,在应该使用自定义getter的情况下,是否有比这更好的模式可用。或者这是Xcode / clang的错误吗?


我创建了一个示例项目在这里,其中包含一个测试用例,展示了这个失败的情况。 - Streeter
1个回答

15

addObserver方法的forKeyPath参数中应传递属性的名称,而不是getter或setter方法:

[self.textView addObserver:self
                forKeyPath:@"editable"
                   options:NSKeyValueObservingOptionNew
                   context:KVOTestingTestsContext];

不需要使用NSStringFromSelector@selector来生成关键路径。实际上,这样做可能会导致你面临的问题...

  1. 关键路径基于属性名称而不是获取器。这是因为你希望KVO拦截setEditable方法。如果您将@"isEditable"(无论如何生成)传递给addObserver,KVO将尝试拦截名为isEditable的属性的setter,并且该属性不存在。

  2. 您使用第二种方法获得通知,因为最终通过引用一个不存在的方法(即editable)来传递@"editable"addObserver,这正是您想要做的,但是编译器会发出警告。

  3. 由于您无法将获取器或设置器方法的名称传递给addObserver,所以答案是直接使用字符串字面量传递属性名称(即@"editable")。

这里是苹果的编程指南供参考:注册键值观察


+1 esp +1 for "godel"。不确定这是否与您的名字有关,但是http://en.wikipedia.org/wiki/Gödel,_Escher,_Bach启发了我学习计算机科学。 - danh
@danh 那正是它的来处,我同意受到启发去学习计算机科学! :-) - godel9
使用 NSStringFromSelector(@selector(...)) 的原因被认为是一种良好的实践,是因为这样可以获得编译器对键存在性的检查。这样可以避免你在意识到键路径中有拼写错误之前,浪费很多时间去思考为什么观察者没有被调用的潜在问题。 - Daniel Rinser
我花了一些时间才真正理解这个问题的原因。就像你所说,KVO想要的是@property名称,虽然通常与属性的getter相同,但并不总是如此。使用NSStringFromSelector(@selector(myProperty))是一个好的但有点hacky的技巧。 - nevan king
此外,大多数人可能从NSHipster的KVO页获得@selector技巧,该页面使用自定义getter名称作为示例: NSStringFromSelector(@selector(isFinished)) - nevan king

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接