11

为什么我不能观察editing一个实例的属性UITableViewController

我正在使用以下代码:

[self addObserver:self 
       forKeyPath:@"editing" 
          options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
          context:NULL];

并实现了方法:

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

...但是observeValueForKeyPath当这个值改变时,这个方法永远不会被调用。


根据 Apple 的确保 KVC 合规性部分:

对于属性或一对一关系的属性,这要求您的类:

  • 实现一个名为-<key>,的方法-is<Key>,或者有一个实例变量<key>or _<key>
  • 如果属性是可变的,那么它也应该实现-set<Key>:.
  • 您对该-set<Key>:方法的实现不应执行验证。
  • -validate<Key>:error:如果验证适用于密钥,您的类应该实现。

editing属性的文档指出,它被定义为:

@property(nonatomic, getter=isEditing) BOOL editing

由于这个属性是不可变的,它必须符合的唯一要点是第一个(-is<Key>例如,定义了一个方法)。通过查看属性的声明,您可以看到它确实符合这一点,并注意到isEditing定义了一个方法。因此,它应该符合 Key Value Observing。它怎么不起作用?

4

2 回答 2

23

您将键值编码合规性与键值观察合规性混淆了。该属性是 KVC 兼容的,这意味着您可以使用[myViewController valueForKey:@"editing"]它来访问它(如果您喜欢打字),但这并不意味着它是 KVO 兼容的。

KV O合规性是通过对象实现符合 KVC 的设置器(要点 2 和 3),KVO 将自动包装,或者通过发送自身will/didChangeValueForKey:消息手动发布 KVO 通知。

UIViewController 和 UITableViewController 不公开实现setEditing:;如果他们根本没有实现它,那么自动包装它的 KVO 就会被淘汰。这留下了手动通知。如果您没有收到该属性的任何 KVO 通知(并且您实际上正在点击该addObserver:forKeyPath:options:context:消息),则表明这些类既没有私下实现setEditing:也没有手动发布 KVO 通知。

因此,该属性是不可观察的。

如果设置editing属性的唯一方法是向控制器发送setEditing:animated:消息,那么您可以setEditing:animated:自己从实现中覆盖并发送 KVO 通知,然后该属性将是可观察的。

于 2010-09-28T22:10:33.953 回答
0

这有点笨拙,但您可以通过观察editButtonItem's来解决这个问题title

[self.viewControllerToObserve addObserver:self forKeyPath:@"editButtonItem.title" options:0 context:kMyViewControllerKVOContext];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == kMyViewControllerKVOContext) {
        // editing changed
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

仅供参考,这就是我声明上下文的方式(上图@implementation):

static void * const kMyViewControllerKVOContext = (void *)&kMyViewControllerKVOContext;

使用Hopper,我们可以看到它在UIViewController's期间setEditing创建了一个新editButtonItem的标题,该标题取决于正在更改的编辑内容。

/* @class UIViewController */
-(void)setEditing:(bool)arg2 animated:(bool)arg3 {
    rdx = arg2;
    rdi = self;
    rax = *ivar_offset(_viewControllerFlags);
    rcx = *(rdi + rax);
    if (((rcx & 0x4) >> 0x2 ^ rdx) == 0x1) {
            stack[-8] = rbp;
            stack[-16] = r15;
            stack[-24] = r14;
            stack[-32] = r13;
            stack[-40] = r12;
            stack[-48] = rbx;
            rsp = rsp - 0x38;
            r12 = rdi;
            *(rdi + rax) = (rcx & 0xfffffffffffffffb) + (rdx & 0xff) * 0x4;
            r15 = [UIBarButtonItem alloc];
            r14 = [__UIKitBundle() retain];
            if ((rdx & 0xff) != 0x0) {
                    rax = [r14 localizedStringForKey:@"Done" value:rcx table:@"Localizable"];
                    rax = [rax retain];
                    r13 = rax;
                    rcx = 0x2;
                    rdi = r15;
                    rdx = rax;
            }
            else {
                    rax = [r14 localizedStringForKey:@"Edit" value:rcx table:@"Localizable"];
                    rax = [rax retain];
                    r13 = rax;
                    rdi = r15;
                    rdx = rax;
                    rcx = 0x0;
            }
            rbx = [rdi initWithTitle:rdx style:rcx target:0x0 action:0x0];
            [r13 release];
            [r14 release];
            [r12->_editButtonItem _setItemVariation:rbx];
            [rbx release];
    }
    return;
}

更多感兴趣的人:

/* @class UIBarButtonItem */
-(void)_setItemVariation:(void *)arg2 {
    rdx = arg2;
    rdi = self;
    rax = *ivar_offset(_barButtonItemFlags);
    if ((*(int8_t *)(rdi + rax) & 0x10) == 0x0) {
            rax = [rdx retain];
            r15 = rax;
            rax = [rax title];
            rax = [rax retain];
            [rdi setTitle:rax];
            [rax release];
            rbx = [r15 style];
            [r15 release];
            [rdi setStyle:rbx];
    }
    return;
}
于 2020-02-24T17:20:23.670 回答