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 のEnsuring KVC Complianceセクションによると:

属性または対 1 の関係であるプロパティの場合、クラスは次のことを行う必要があります。

  • -<key>、 という名前のメソッドを実装する-is<Key>か、インスタンス変数<key>またはを持ちます_<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

Key-Value Codingコンプライアンスと Key-Value Observingコンプライアンスを混同しています。このプロパティは KVC に準拠して[myViewController valueForKey:@"editing"]いるため、(入力が好きな場合) を使用してアクセスできますが、KVO に準拠しているわけではありません。

KV O準拠は、KVO が自動的にラップする KVC 準拠のセッター (箇条書き 2 と 3) を実装するか、オブジェクト自体will/didChangeValueForKey:メッセージを送信して手動で KVO 通知を投稿することによって達成されます。

UIViewController と UITableViewController は公に実装されていませんsetEditing:。彼らがそれをまったく実装しない場合、それを自動的にラップする KVO は無効になります。これにより、手動通知が残ります。プロパティの KVO 通知を受け取っていない (実際にそのaddObserver:forKeyPath:options:context:メッセージが表示されている) 場合は、これらのクラスが KVO 通知を非公開で実装しsetEditing:たり、手動で投稿したりしていないことを示しています。

したがって、プロパティは観察できません。

プロパティを設定する唯一の方法editingがコントローラーにメッセージを送信することである場合、実装から KVO 通知setEditing:animated:をオーバーライドして送信すると、プロパティが監視可能になります。setEditing:animated:

于 2010-09-28T22:10:33.953 に答える
0

少しぎこちないですが、 を観察することでこれを回避できeditButtonItemます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を使用すると、変更された編集内容に応じたタイトルで、 duringUIViewControllersetEditing作成されることがわかります。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 に答える