Obj-c Programming 17: key value observation (KVO)

Source: Internet
Author: User

Obj-c Programming 17: key value observation (KVO)

After finishing the previous article on KVC, I can't help but talk about its application KVO (Key-Value Observing. KVO is similar to the hook function in ruby. When an object property changes, the observer can track the changes and then observe or modify the changes, this is done through the callback function registered by the callback observer. To use key-value observation, three conditions must be met:

1. The observed object must use the KVC-compliant accessors for the observed attributes;

2. The observer must implement the notification receiving method (callback method):-observeValue: forKeyPath: ofObject: change: context:. This method is called when the value changes and can be customized: for example, receiving new and original values and other custom information at the same time;

3. The observer registers a specific path of a specific object through the-addObserver: forKeyPath: options: context: method.

It should also be noted that after the observer completes the observation of the observer, he must remove himself; otherwise, after the observer is released, the notification message from the change of the observer will be nowhere to be sent, the reference may crash. In addition, multiple observers can be used to observe the same path of the same object, similar to forming an observation chain.

Below I will write a simple example to illustrate the above content with code:


#import

#define msg (...) NSLog (__ VA_ARGS__)
#define mki (x) [NSNumber numberWithInt: x]

@interface Son: NSObject {
NSString * girl_friend_name;
}
@property NSString * girl_friend_name;
-(void) think;
-(void) fall_in_love_with: (NSString *) girl_name;
@end

@implementation Son
@synthesize girl_friend_name;

-(void) fall_in_love_with: (NSString *) girl_name {
self.girl_friend_name = girl_name;
}

-(void) think {
msg (@ "My love girl is% @ ... Does baba know?", girl_friend_name);
}
@end

@interface Baba: NSObject {
Son * son;
}
@property Son * son;
@end

@implementation Baba
@synthesize son;

-(id) initWithSon: (Son *) son_v {
self = [super init];
if (self) {
son = son_v;
[son addObserver: self forKeyPath: @ "girl_friend_name" \
options: (NSKeyValueObservingOptionNew | \
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial) \
context: nil];
}
return self;
}

-(void) observeValueForKeyPath: (NSString *) key_path \
ofObject: (id) obj change: (NSDictionary *) change \
context: (void *) context {

NSString * old_name = [change objectForKey: NSKeyValueChangeOldKey];
NSString * new_name = [change objectForKey: NSKeyValueChangeNewKey];

if (! old_name) {
// NSKeyValueObservingOptionInitial option, the first hook will send a message
// In the future, a message will be sent for each change.
msg (@ "Yes, It's first time to induction son's girl_name !!!");
} else {
if ([new_name isEqualToString: @ "libinbin"]) {
[obj fall_in_love_with: @ "fanbinbin"];
}
msg (@ "My son's% @ change from% @ to% @ ... That's OK? :(", \
key_path, old_name, new_name);
}
}

-(void) dealloc {
[son removeObserver: self forKeyPath: @ "girl_friend_name"];
son = nil;
// [super dealloc];
}
@end

int main (int argc, char * argv [])
{
@autoreleasepool {
Son * son = [[Son alloc] init];
[son fall_in_love_with: @ "lili"];
[son think];

Baba * baba = [[Baba alloc] initWithSon: son];
[son fall_in_love_with: @ "mimi"];
[son think];

// My son originally liked libinbin, but ...
[son fall_in_love_with: @ "libinbin"];
// It was changed to fanbinbin by his dad with a special function!
[son think];
}
return 0;
}

To put it simply, Dad naturally wants to monitor his son's girlfriend at any time, but this dad can even monitor him and forcibly change his son's girlfriend... just consider fun as an example. Description:

-AddObserver registers the observer of the object. Other optional values of options are as follows. You can select multiple values:


The context parameter is of unknown significance. If anyone knows, don't forget to tell me about it. For the meaning of the NSKeyValueObservingOptionInitial flag, see code comments.

-Change in the observeValueForKeyPath callback method is a dictionary parameter, including:


The NSKeyValueChangeKindKey specifies the type of change received, which may have the following values: <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4KPHA + pgltzybzcm9 "http://www.2cto.com/uploadfile/Collfiles/20140707/20140707084335219.png" alt = "\">

The code in the book adds a backtracking call to the method with the same name in the parent class at the end of the method definition:

[Super observeValueForkeyPath: key_path ofObject: obj change: change context: ctx];

But I did not write this in the code. In the book, the observer's dealloc method finally has a [super dealloc], but this will cause errors during compilation under ARC. Remove it temporarily. The code execution result is as follows:


Apple @ kissAir: objc_src $./k

20:11:26. 757 k [2109:507] My love girl is lili... Does baba know?

20:11:26. 759 k [2109:507] Yes, It "s first time to induction son's girl_name !!!

20:11:26. 760 k [2109:507] My son's girl_friend_name change from lili to mimi... That's OK? :(

20:11:26. 760 k [2109:507] My love girl is mimi... Does baba know?

20:11:26. 761 k [2109:507] My son's girl_friend_name change from libinbin to fanbinbin... That's OK? :(

20:11:26. 761 k [2109:507] My son's girl_friend_name change from mimi to libinbin... That's OK? :(

20:11:26. 761 k [2109:507] My love girl is fanbinbin... Does baba know?

Dad has good supervision, but his son is not happy. How can the son of hack make his father so infringe on his privacy? What should we do? If the change notification is not automatically passed, it will not end! To implement manual notifications, You need to override Son's class method + automaticallyNotifiesObserversForKey to inform obj-c that you do not want to automatically notify the observer of your changes. Note that this is a class method! You can return NO for a specific key name:


+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
		if([key isEqualToString:@"girl_friend_name"])
			return NO;
		return YES;
	}


Add the above class methods to the Son class implementation and re-compile and run them:


Apple @ kissAir: objc_src $./k

20:37:36. 726 k [2180: 507] My love girl is lili... Does baba know?

20:37:36. 729 k [2180: 507] Yes, It's first time to induction son's girl_name !!!

20:37:36. 729 k [2180: 507] My love girl is mimi... Does baba know?

20:37:36. 730 k [2180: 507] My love girl is libinbin... Does baba know?

Dad, "No! But if this is so long, dad will doubt it. Is it swollen? Is my baby son never in love? This cannot be done! So the smart hack son can manually notify his dad of the tampered content! Specifically: Call-willChangeValueForKey before the Son property writer method changes, then call-didChangeValueForKey after the change, and rewrite the writer method. The complete code after the modification is as follows:


#import

#define msg (...) NSLog (__ VA_ARGS__)
#define mki (x) [NSNumber numberWithInt: x]

@interface Son: NSObject {
NSString * girl_friend_name;
}
// In the case of the default attribute is atomic, if the custom writer method must also customize the reader method,
// Or change the attribute to nonatomic mode.
@property (nonatomic) NSString * girl_friend_name;
-(void) think;
-(void) fall_in_love_with: (NSString *) girl_name;
@end

@implementation Son
@synthesize girl_friend_name;

-(void) fall_in_love_with: (NSString *) girl_name {
self.girl_friend_name = girl_name;
}

-(void) think {
msg (@ "My love girl is% @ ... Does baba know?", girl_friend_name);
}

+ (BOOL) automaticallyNotifiesObserversForKey: (NSString *) key {
if ([key isEqualToString: @ "girl_friend_name"])
return NO;
return YES;
}

-(void) setGirl_friend_name: (NSString *) name {
[self willChangeValueForKey: @ "girl_friend_name"];
// Let dad think that he is in love with female Xueba
girl_friend_name = @ "女 学 霸";
[self didChangeValueForKey: @ "girl_friend_name"];
// Restore it, BAD BOY ... :)
girl_friend_name = name;
}
@end

@interface Baba: NSObject {
Son * son;
}
@property Son * son;
@end

@implementation Baba
@synthesize son;

-(id) initWithSon: (Son *) son_v {
self = [super init];
if (self) {
son = son_v;
[son addObserver: self forKeyPath: @ "girl_friend_name" \
options: (NSKeyValueObservingOptionNew | \
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial) \
context: nil];
}
return self;
}

-(void) observeValueForKeyPath: (NSString *) key_path \
ofObject: (id) obj change: (NSDictionary *) change \
context: (void *) context {

NSString * old_name = [change objectForKey: NSKeyValueChangeOldKey];
NSString * new_name = [change objectForKey: NSKeyValueChangeNewKey];

if (! old_name) {
// NSKeyValueObservingOptionInitial option, the first hook will send a message
// In the future, a message will be sent for each change.
msg (@ "Yes, It's first time to induction son's girl_name !!!");
} else {
if ([new_name isEqualToString: @ "libinbin"]) {
[obj fall_in_love_with: @ "fanbinbin"];
}
msg (@ "My son's% @ change from% @ to% @ ... That's OK? :(", \
key_path, old_name, new_name);
}
}

-(void) dealloc {
[son removeObserver: self forKeyPath: @ "girl_friend_name"];
son = nil;
// [super dealloc];
}
@end

int main (int argc, char * argv [])
{
@autoreleasepool {
Son * son = [[Son alloc] init];
[son fall_in_love_with: @ "lili"];
[son think];

Baba * baba = [[Baba alloc] initWithSon: son];
[son fall_in_love_with: @ "mimi"];
[son think];

// My son originally liked libinbin, but ...
[son fall_in_love_with: @ "libinbin"];
// It was changed to fanbinbin by his dad with a special function!
[son think];
}
return 0;
} 


The compilation and running results are as follows:


Apple @ kissAir: objc_src $./k

20:58:59. 284 k [2270: 507] My love girl is lili... Does baba know?

20:58:59. 286 k [2270: 507] Yes, It's first time to induction son's girl_name !!!

20:58:59. 287 k [2270: 507] My son's girl_friend_name change from lili to female students... That's OK? :(

20:58:59. 287 k [2270: 507] My love girl is mimi... Does baba know?

20:58:59. 288 k [2270: 507] My son's girl_friend_name change from mimi to female students... That's OK? :(

20:58:59. 288 k [2270: 507] My love girl is libinbin... Does baba know?

This time, the father was dumb and thought that his son had been in a bid with the female schoolmaster. This is also true for all the kids shoes.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.