kvc与kvo
2013-10-13 22:13
399 查看
kvc:key value coding,键值编码。
通过键值对的形势访问对象的属性:
使用实例:使用xcode创建新的project,选择osx,application中的commond line tool
,选择type为Foundation;创建一个Person类。
Person.h
Person.m
没有设置property所以无法直接获取Person对象的name属性,这时可以通过kvc的方式访问对象。
打印输出
2013-10-13 22:07:27.211 KVCKVO[4323:303] (null)
2013-10-13 22:07:27.213 KVCKVO[4323:303] xiaoxiao
在kvc中,valueForKey方法先尝试在person实例上找getName方法,如果找到就调用。如果没有找到,则查找实例是否有name变量或者_name变量。如果还没找到,会抛出异常:
2013-10-13 20:45:50.888 KVCKVO[3984:303] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x10010a5b0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name1.'
这里可以在Person类中添加方法来避免抛出异常
重新为Person类添加一个属性School,
School.h
Person.h
School.m和Person.m依然什么都不写
以上为kvc的基本用法。
kvo:Key Value Observing,直译为:基于键值的观察。
依然沿用上面的例子,在ios开发中,person对象可能与多个界面属性相关,这时可以通过注册观察者的方式来观察person类的属性,一旦属性发生变化,界面就可以作出响应。
这里我们为Person类添加一个属性,int age,新建一个observer类来当作观察者观察这个属性。
Observer.h
Observer.m
main.m
输出结果为
2013-10-13 21:19:58.270 KVCKVO[4150:303] age changed
2013-10-13 21:20:01.214 KVCKVO[4150:303] old value:0
2013-10-13 21:20:01.215 KVCKVO[4150:303] new value:25
注意根据具体情况来调用,比如observer释放的时候
来移出kvo,避免内存泄漏。
手动键值观察:
还是上面的例子:
Person.h
Person.m
4000
}@end
得到输出:
2013-10-13 21:40:22.931 KVCKVO[4221:303] age changed
2013-10-13 21:40:22.932 KVCKVO[4221:303] old value:0
2013-10-13 21:40:22.932 KVCKVO[4221:303] new value:25
2013-10-13 21:40:22.933 KVCKVO[4221:303] age changed
2013-10-13 21:40:22.934 KVCKVO[4221:303] old value:25
2013-10-13 21:40:22.934 KVCKVO[4221:303] new value:30
注意需要实现函数
在对应的key来返回no,否则调用super的改方法。
自动键值观察:
使用@property来使用属性。
键值观察依赖键:
有时候观察者依赖于被观察者的多个属性,这时可以使用键值观察依赖。
这里需要新建一个实现依赖键:
targetWrapper.h
targetWrapper.m
main.m添加代码
输出结果为:
2013-10-13 22:07:27.219 KVCKVO[4323:303] information changed
2013-10-13 22:07:27.220 KVCKVO[4323:303] old value:30^whut
2013-10-13 22:07:27.221 KVCKVO[4323:303] new value:45^whut
2013-10-13 22:07:27.221 KVCKVO[4323:303] information changed
2013-10-13 22:07:27.222 KVCKVO[4323:303] old value:45^whut
2013-10-13 22:07:27.222 KVCKVO[4323:303] new value:45^hust
要实现 keyPathsForValuesAffectingInformation 或 keyPathsForValuesAffectingValueForKey:
方法来告诉系统 information 属性依赖于哪些其他属性,这两个方法都返回一个key-path 的集合
在这里,information
属性依赖于 person 的 age 和 school 属性,person 的 age/school 属性任一发生变化,information 的观察者都会得到通知。
键值观察用处很多,Core Binding 背后的实现就有它的身影,那键值观察背后的实现又如何呢?想一想在上面的自动实现方式中,我们并不需要在被观察对象 Target 中添加额外的代码,就能获得键值观察的功能,这很好很强大,这是怎么做到的呢?答案就是 Objective C 强大的 runtime 动态能力,下面我们一起来窥探下其内部实现过程。
当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
引用
http://www.cnblogs.com/kesalin/archive/2012/11/17/KVO.html
通过键值对的形势访问对象的属性:
//读取属性 - (id)valueForKey:(NSString *)key; //设置属性 - (void)setValue:(id)value forKey:(NSString *)key;
使用实例:使用xcode创建新的project,选择osx,application中的commond line tool
,选择type为Foundation;创建一个Person类。
Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject { NSString *name; } @end
Person.m
#import "Person.h" @implementation Person @end
没有设置property所以无法直接获取Person对象的name属性,这时可以通过kvc的方式访问对象。
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... Person *person = [[Person alloc] init]; NSLog(@"%@",[person valueForKey:@"name"]); [person setValue:@"xiaoxiao" forKey:@"name"]; NSLog(@"%@",[person valueForKey:@"name"]); } return 0; }
打印输出
2013-10-13 22:07:27.211 KVCKVO[4323:303] (null)
2013-10-13 22:07:27.213 KVCKVO[4323:303] xiaoxiao
在kvc中,valueForKey方法先尝试在person实例上找getName方法,如果找到就调用。如果没有找到,则查找实例是否有name变量或者_name变量。如果还没找到,会抛出异常:
2013-10-13 20:45:50.888 KVCKVO[3984:303] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x10010a5b0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name1.'
这里可以在Person类中添加方法来避免抛出异常
- (id)valueForUndefinedKey:(NSString *)key - (void)setValue:(id)value forUndefinedKey:(NSString *)key
重新为Person类添加一个属性School,
School.h
#import <Foundation/Foundation.h> @interface School : NSObject { NSString *schoolName; } @end
Person.h
#import <Foundation/Foundation.h> #import "School.h" @interface Person : NSObject { NSString *name; School *school; } @end
School.m和Person.m依然什么都不写
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... Person *person = [[Person alloc] init]; NSLog(@"%@",[person valueForKey:@"name"]); [person setValue:@"misery" forKey:@"name"]; NSLog(@"%@",[person valueForKey:@"name"]); School *school = [[School alloc] init]; [person setValue:school forKey:@"school"]; NSLog(@"%@",[person valueForKeyPath:@"school.schoolName"]); [person setValue:@"whut" forKeyPath:@"school.schoolName"]; NSLog(@"%@",[person valueForKeyPath:@"school.schoolName"]); } return 0; }
以上为kvc的基本用法。
kvo:Key Value Observing,直译为:基于键值的观察。
依然沿用上面的例子,在ios开发中,person对象可能与多个界面属性相关,这时可以通过注册观察者的方式来观察person类的属性,一旦属性发生变化,界面就可以作出响应。
这里我们为Person类添加一个属性,int age,新建一个observer类来当作观察者观察这个属性。
Observer.h
#import <Foundation/Foundation.h> @interface Observer : NSObject @end
Observer.m
#import "Observer.h" @implementation Observer - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"age"]) { NSLog(@"age changed"); NSLog(@"old value:%@",[change objectForKey:@"old"]); NSLog(@"new value:%@",[change objectForKey:@"new"]); } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } @end
main.m
#import <Foundation/Foundation.h> #import "Person.h" #import "Observer.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... Person *person = [[Person alloc] init]; NSLog(@"%@",[person valueForKey:@"name"]); [person setValue:@"misery" forKey:@"name"]; NSLog(@"%@",[person valueForKey:@"name"]); School *school = [[School alloc] init]; [person setValue:school forKey:@"school"]; NSLog(@"%@",[person valueForKeyPath:@"school.schoolName"]); [person setValue:@"whut" forKeyPath:@"school.schoolName"]; NSLog(@"%@",[person valueForKeyPath:@"school.schoolName"]); Observer *observer = [[Observer alloc] init]; //注册kvo [person addObserver:observer forKeyPath:@"age" options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void *)([Person class])]; [person setValue:[NSNumber numberWithInteger:25] forKey:@"age"]; //移出kvo [person removeObserver:observer forKeyPath:@"age"]; } return 0; }
输出结果为
2013-10-13 21:19:58.270 KVCKVO[4150:303] age changed
2013-10-13 21:20:01.214 KVCKVO[4150:303] old value:0
2013-10-13 21:20:01.215 KVCKVO[4150:303] new value:25
注意根据具体情况来调用,比如observer释放的时候
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
来移出kvo,避免内存泄漏。
手动键值观察:
还是上面的例子:
Person.h
#import <Foundation/Foundation.h> #import "School.h" @interface Person : NSObject { NSString *name; School *school; int age; } - (int)age; - (void)setAge:(int)age; @end
Person.m
#import "Person.h" @implementation Person - (int)age { return age; } - (void)setAge:(int)theAge { [self willChangeValueForKey:@"age"]; age = theAge; [self didChangeValueForKey:@"age"]; }
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{ if ([key isEqualToString:@"age"]) { return NO; } else { return [super automaticallyNotifiesObserversForKey:key]; }
4000
}@end
[person setAge:30];
得到输出:
2013-10-13 21:40:22.931 KVCKVO[4221:303] age changed
2013-10-13 21:40:22.932 KVCKVO[4221:303] old value:0
2013-10-13 21:40:22.932 KVCKVO[4221:303] new value:25
2013-10-13 21:40:22.933 KVCKVO[4221:303] age changed
2013-10-13 21:40:22.934 KVCKVO[4221:303] old value:25
2013-10-13 21:40:22.934 KVCKVO[4221:303] new value:30
注意需要实现函数
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
在对应的key来返回no,否则调用super的改方法。
自动键值观察:
使用@property来使用属性。
键值观察依赖键:
有时候观察者依赖于被观察者的多个属性,这时可以使用键值观察依赖。
这里需要新建一个实现依赖键:
targetWrapper.h
#import <Foundation/Foundation.h> #import "Person.h" @interface targetWrapper : NSObject { Person *tperson; } @property(nonatomic,assign) NSString *information; - (id)init:(Person *)theperson; @end
targetWrapper.m
#import "targetWrapper.h" @implementation targetWrapper - (id)init:(Person *)aperson { self = [super init]; if (self) { tperson = aperson; } return self; } - (id)information { return [NSString stringWithFormat:@"%d^%@",[tperson age],[tperson valueForKeyPath:@"school.schoolName"]]; } - (void)setInformation:(NSString *)theInformation { NSArray * array = [theInformation componentsSeparatedByString:@"^"]; [tperson setAge:[[array objectAtIndex:0] intValue]]; [tperson setValue:[array objectAtIndex:1] forKeyPath:@"school.schoolName"]; } + (NSSet *)keyPathsForValuesAffectingInformation { return [NSSet setWithObjects:@"tperson.age",@"tperson.school",nil]; } @end
main.m添加代码
//依赖 targetWrapper *tw = [[targetWrapper alloc] init:person]; [tw addObserver:observer forKeyPath:@"information" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void *)([targetWrapper class])]; [person setAge:45]; School *school2 = [[School alloc] init]; [school2 setValue:@"hust" forKey:@"schoolName"]; [person setValue:school2 forKeyPath:@"school"]; [tw removeObserver:observer forKeyPath:@"information"];
输出结果为:
2013-10-13 22:07:27.219 KVCKVO[4323:303] information changed
2013-10-13 22:07:27.220 KVCKVO[4323:303] old value:30^whut
2013-10-13 22:07:27.221 KVCKVO[4323:303] new value:45^whut
2013-10-13 22:07:27.221 KVCKVO[4323:303] information changed
2013-10-13 22:07:27.222 KVCKVO[4323:303] old value:45^whut
2013-10-13 22:07:27.222 KVCKVO[4323:303] new value:45^hust
要实现 keyPathsForValuesAffectingInformation 或 keyPathsForValuesAffectingValueForKey:
方法来告诉系统 information 属性依赖于哪些其他属性,这两个方法都返回一个key-path 的集合
在这里,information
属性依赖于 person 的 age 和 school 属性,person 的 age/school 属性任一发生变化,information 的观察者都会得到通知。
kvo实现机理
键值观察用处很多,Core Binding 背后的实现就有它的身影,那键值观察背后的实现又如何呢?想一想在上面的自动实现方式中,我们并不需要在被观察对象 Target 中添加额外的代码,就能获得键值观察的功能,这很好很强大,这是怎么做到的呢?答案就是 Objective C 强大的 runtime 动态能力,下面我们一起来窥探下其内部实现过程。当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
引用
http://www.cnblogs.com/kesalin/archive/2012/11/17/KVO.html
相关文章推荐
- KVC 和KVO浅谈
- KVC和KVO的基本使用
- KVC 与 KVO 理解
- iOS开发系列--Objective-C之KVC、KVO
- Objective-C之KVC、KVO
- KVC,KVO详解
- KVO 与 KVC
- 12.代理、通知、KVC\KVO
- 设计模式kvc和kvo
- HUD指示器跳出的问题以及KVC和KVO
- KVO and KVC
- Objective-C中的KVC和KVO
- Object-C基础(8)——KVC、KVO与继承
- iOS KVC & KVO
- 谈KVC、KVO(重点观察者模式)机制编程
- KVC、KVO、NSNotification、delegate 总结及区别
- iOS KVC 和 KVO 区别简单总结
- KVC/KVO原理及应用(下)
- KVC/KVO
- OC中的KVC和KVO(一)