What is Key-Value-Coding and Key-Value-Observing in Objective C?

倾然丶 夕夏残阳落幕 提交于 2019-12-02 14:04:10

Key-Value-Coding (KVC) means accessing a property or value using a string.

id someValue = [myObject valueForKeyPath:@"foo.bar.baz"];

Which could be the same as:

id someValue = [[[myObject foo] bar] baz];

Key-Value-Observing (KVO) allows you to observe changes to a property or value.

To observe a property using KVO you would identify to property with a string; i.e., using KVC. Therefore, the observable object must be KVC compliant.

[myObject addObserver:self forKeyPath:@"foo.bar.baz" options:0 context:NULL];

Key Value Coding is simply accessing a property of an object through a string instead of the literal syntax.

// Here is a new instance of an object
Foo *foo = [[Foo alloc] init];
// Accessing a property called someValue with literal syntax:
[foo someValue];
// Accessing the same property with dot notation
foo.someValue;
// Accessing the same property with Key-Value coding:
[foo valueForKey:@"someValue"];

The power of KVC is that you can specify any arbitrary string at runtime (obviously this could be very dangerous too).

Key-value coding allows you to fetch or change a property of an object using a string, at runtime, instead of needing to write code that is compiled to a fixed property from the start:

NSNumber* foo = [myPopup valueForKey: @"selectedItemIndex"];
[myPopup setValue: @15 forKey: @"selectedItemIndex"];

A good example for this is NSTableView on Mac, where you can just set an identifier on every table column that corresponds to your model object's property that it should display, and then your data source just calls -valueForKey:/-setValue:forKey: with the column's identifier as the key and the values pretty much display/set themselves. You just add the right columns to the table view in the XIB.

Key-value observing was added afterwards, and lets you register to be notified about changes made to another object. You register your interest by doing:

void*    gMyKVOContext = &gMyKVOContext; // global variable somewhere that guarantees us a unique address that doesn't collide with a subclass's registration for observing the same property

...

[interestingObject addObserver: interestedObject forKeyPath: @"interestingProperty" options: 0 context: gMyKVOContext];

Whenever that property is changed, -observeValueForKeyPath:ofObject:change:context: will be called on the object you specified as the observer. So you'd implement that like:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if( context == gMyKVOContext && [keyPath isEqualToString: @"interestingProperty"] )
    {
        // Update UI that shows interestingProperty
    }
    else
        [super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
}

The advantage here is that you get called live the moment that other property is changed. Note that objects have to do a little work so these notifications are sent, so not all properties are key-value-observable. Also note that some objects may be in an invalid state if two related properties get changed right after the other: You get notified after the first property has been changed, which now contradicts the second, and only then the second property is changed and you're notified for that. So during that first observer callback, the object may be in a weird state, so be careful how you react to that.

To make a property observable, either use the default @synthesized implementation when you define it, or if you define it yourself, implement the setter like:

-(void)  setFoo: (int)inFoo
{
    [self willChangeValueForKey: @"foo"];
    _foo = inFoo;
    [self didChangeValueForKey: @"foo"];
}

Then always go through the setter to change it, don't change _foo directly. If you have related properties that could contradict each other like the above, a good way to avoid this is to always change them both in pairs (you can't use KVC then, though). To do that, implement a combined setter like:

-(void) setFoo: (int)inFoo bar: (int)inBar
{
    [self willChangeValueForKey: @"foo"];
    [self willChangeValueForKey: @"bar"];
    _foo = inFoo;
    _bar = inBar;
    [self didChangeValueForKey: @"bar"];
    [self didChangeValueForKey: @"foo"];
}

That way, both notifications are sent while the properties are in proper states.

NSResponder

Start here.

Key-value coding is a mechanism for accessing an object’s properties indirectly, using strings to identify properties, rather than through invocation of an accessor method or accessing them directly through instance variables.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!