Posted in code with : ios, objective-c
Contents:
In Cocoa, the Model-View-Controller pattern, a controller’s responsibility is to keep the view and the model synchronized. There are two parts to this: when the model object changes, the views have to be updated to reflect this change, and when the user interacts with controls, the model has to be updated accordingly.
Key-Value Observing helps us update the views to reflect changes to model objects. The controller can observe changes to those property values that the views depend on.
For more details, refer Key-Value Coding and Observing from objc.io;
KVC, which means NSKeyValueCoding, is a protoco, and supplies accessors (getter and setter) for getting and setting property value. Only by using the KVC setter method to set the property value, can the sender send a message to the observer.
KVC has the following two getter methods: valueForKey:
and valueForKeyPath:
, two setter methods: setValue:forKey:
and setValue:forKeyPath:
.
Assume that Person
class has two simple properties: name
and address
and a Person
type property spouse
. We have the following two pieces of code explaining the Key and KeyPath:
For Key:
1 void changeName(Person *p, NSString *newName)
2 {
3 // using the KVC accessor (getter) method
4 NSString *originalName = [p valueForKey:@"name"];
5
6 // using the KVC accessor (setter) method.
7 [p setValue:newName forKey:@"name"];
8
9 NSLog(@"Changed %@'s name to: %@", originalName, newName);
10 }
For KeyPath:
1 void logMarriage(Person *p)
2 {
3 // just using the accessor again, same as example above
4 NSString *personsName = [p valueForKey:@"name"];
5
6 // this line is different, because it is using
7 // a "key path" instead of a normal "key"
8 NSString *spousesName = [p valueForKeyPath:@"spouse.name"];
9
10 NSLog(@"%@ is happily married to %@", personsName, spousesName);
11 }
Actually, [p valueForKeyPath:@"spouse.name"];
equals to [[p valueForKey:@"spouse"] valueForKey:@"name"];
.
Key Value Observer (KVO) is based on KVC, and can observe the change of a property of another object.
KVO allows you to register as an observer of a given object and receive notification when specific properties on that object are changed. It’s an incredibly powerful capability, and it is built into Objective-C at its very core.
Implement PersonWatcher
for observing a Person
instance.
1 @implementation PersonWatcher
2
3 static NSString *const KVO_CONTEXT_ADDRESS_CHANGED = @"KVO_CONTEXT_ADDRESS_CHANGED";
4
5 -(id) init;
6 {
7 if(self = [super init]){
8 self.m_observedPeople = [NSMutableArray new];
9 }
10
11 return self;
12 }
13
14 // watch a person
15 -(void) watchPersonForChangeOfAddress:(Person *)p
16 {
17 // this begins the observing
18 [p addObserver:self
19 forKeyPath:@"address"
20 options:0
21 context:CFBridgingRetain(KVO_CONTEXT_ADDRESS_CHANGED)];
22
23 // keep a record of all the people being observed,
24 // because we need to stop observing them in dealloc
25 [self.m_observedPeople addObject:p];
26 }
27
28 // whenever an observed key path changes, this method will be called
29 - (void)observeValueForKeyPath:(NSString *)keyPath
30 ofObject:(id)object
31 change:(NSDictionary *)change
32 context:(void *)context
33 {
34 // use the context to make sure this is a change in the address,
35 // because we may also be observing other things
36 if(context == CFBridgingRetain(KVO_CONTEXT_ADDRESS_CHANGED)) {
37 NSString *name = [object valueForKey:@"name"];
38 NSString *address = [object valueForKey:@"address"];
39 NSLog(@"%@ has a new address: %@", name, address);
40 }
41 }
42
43 -(void) dealloc;
44 {
45 // must stop observing everything before this object is
46 // deallocated, otherwise it will cause crashes
47 for(Person *p in self.m_observedPeople){
48 [p removeObserver:self forKeyPath:@"address"];
49 }
50
51 self.m_observedPeople = nil;
52 }
Refer here
You should stop observing the sender when observer is dealloced. If you fail to do this and then allow the observer to be deallocated, then future notifications to the observer may cause your application to crash.
So, remember to remove observers
For #1
, just send removeObserver:forKeyPath
message to the sender in the -dealloc
function of the observer.
-dealloc
function is called even in ARC mode. In -dealloc
, just free non-object resources, or clean up tasks like removing observers. In -dealloc
under ARC mode, you can not call [super dealloc]
, as the compiller did it for you and this why there is an error if you call this manually.
Note:
-dealloc
is not called in garbage collection mode.
For #2
, the observer must know the life circle of the sender, and before the sender is freed, the observer must remove the observation from the sender.