iOS下KVO的使用以及一些实现细节
来源:互联网 发布:怎样举报淘宝店铺 编辑:程序博客网 时间:2024/05/20 20:44
KVO的是Key Value Observe的缩写,中文是键值观察。这是一个典型的观察者模式,观察者在键值改变时会得到通知。iOS中有个Notification的机制,也可以获得通知,但这个机制需要有个Center,相比之下KVO更加简洁而直接。
KVO的使用也很简单,就是简单的3步。
1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:
2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用
3.取消注册观察removeObserver:forKeyPath:context:
不多说了,上代码:
第一段代码声明了myPerson类,里面有个_height的属性。在testViewController有一个testPerson的对象指针。
在testKVO这个方法里面,我们注册了testPerson这个对象height属性的观察,这样当testPerson的height属性变化时,会得到通知。在这个方法中还通过NSKeyValueObservingOptionNew这个参数要求把新值在dictionary中传递过来。
重写了observeValueForKeyPath:ofObject:change:context:方法,这个方法里的change这个NSDictionary对象包含了相应的值。
需要强调的是KVO的回调要被调用,属性必须是通过KVC的方法来修改的,如果是调用类的其他方法来修改属性,这个观察者是不会得到通知的。
因为Cocoa是严格遵循MVC模式的,所以KVO在观察Modal的数据变化时很有用。那么KVO是怎么实现的呢,苹果官方文档上说的比较简单:“Automatic key-value observing is implemented using a technique called isa-swizzling.”
“When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.”
就是说在运行时会生成一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法,用来欺骗系统顶替原先的类。
继续观察一下代码:
略微改写了一下myPerson,age/height/weight两个属性增加了getter/setter方法,然后运用runtime的方法,打印相应的内容,运行的log如下:
2013-11-02 20:36:22.391 test[2438:c07]
normal: <myPerson: 0x886b840>
NSObject class myPerson
libobjc class myPerson
implements methods <weight, setWeight:, age, setAge:, height, setHeight:>
2013-11-02 20:36:22.393 test[2438:c07]
hObserver: <myPerson: 0x886b7e0>
NSObject class myPerson
libobjc class NSKVONotifying_myPerson
implements methods <setWeight:, setHeight:, class, dealloc, _isKVOA>
2013-11-02 20:36:22.393 test[2438:c07]
wObserver: <myPerson: 0x886b800>
NSObject class myPerson
libobjc class NSKVONotifying_myPerson
implements methods <setWeight:, setHeight:, class, dealloc, _isKVOA>
2013-11-02 20:36:22.393 test[2438:c07]
hwOBserver: <myPerson: 0x886b820>
NSObject class myPerson
libobjc class NSKVONotifying_myPerson
implements methods <setWeight:, setHeight:, class, dealloc, _isKVOA>
2013-11-02 20:36:22.394 test[2438:c07]
Using NSObject methods, normal setHeight: is 0x37e0, overridden setHeight: is 0x37e0
2013-11-02 20:36:22.394 test[2438:c07]
Using libobjc functions, normal setHeight: is 0x37e0, overridden setHeight: is 0xb859e0
从log信息可以清楚的看到派生了一个NSKVONotifying_XXX的类,这个派生类集合了每个KVO观察者的信息,所以这个派生类可以全局公用。
另外,观察原来类的方法和派生类的方法,每个被观察的属性都重写了,比如:setWeight:方法和setHeight:方法,没被观察的属性都没有重新生成,比如:height:方法、weight:方法、age:方法和setAge:方法。
KVO的使用也很简单,就是简单的3步。
1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:
2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用
3.取消注册观察removeObserver:forKeyPath:context:
不多说了,上代码:
- @interface myPerson : NSObject
- {
- NSString *_name;
- int _age;
- int _height;
- int _weight;
- }
- @end
- @interface testViewController : UIViewController
- @property (nonatomic, retain) myPerson *testPerson;
- - (IBAction)onBtnTest:(id)sender;
- @end
- - (void)testKVO
- {
- testPerson = [[myPerson alloc] init];
- [testPerson addObserver:self forKeyPath:@"height" options:NSKeyValueObservingOptionNew context:nil];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- {
- if ([keyPath isEqualToString:@"height"]) {
- NSLog(@"Height is changed! new=%@", [change valueForKey:NSKeyValueChangeNewKey]);
- } else {
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
- }
- }
- - (IBAction)onBtnTest:(id)sender {
- int h = [[testPerson valueForKey:@"height"] intValue];
- [testPerson setValue:[NSNumber numberWithInt:h+1] forKey:@"height"];
- NSLog(@"person height=%@", [testPerson valueForKey:@"height"]);
- }
- - (void)dealloc
- {
- [testPerson removeObserver:self forKeyPath:@"height" context:nil];
- [super dealloc];
- }
在testKVO这个方法里面,我们注册了testPerson这个对象height属性的观察,这样当testPerson的height属性变化时,会得到通知。在这个方法中还通过NSKeyValueObservingOptionNew这个参数要求把新值在dictionary中传递过来。
重写了observeValueForKeyPath:ofObject:change:context:方法,这个方法里的change这个NSDictionary对象包含了相应的值。
需要强调的是KVO的回调要被调用,属性必须是通过KVC的方法来修改的,如果是调用类的其他方法来修改属性,这个观察者是不会得到通知的。
因为Cocoa是严格遵循MVC模式的,所以KVO在观察Modal的数据变化时很有用。那么KVO是怎么实现的呢,苹果官方文档上说的比较简单:“Automatic key-value observing is implemented using a technique called isa-swizzling.”
“When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.”
就是说在运行时会生成一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法,用来欺骗系统顶替原先的类。
继续观察一下代码:
- @interface myPerson : NSObject
- {
- NSString *_name;
- }
- @property (nonatomic)int height;
- @property (nonatomic)int weight;
- @property (nonatomic)int age;
- @end
- @implementation myPerson
- @synthesize height, weight, age;
- @end
- #import "objc/runtime.h"
- static NSArray * ClassMethodNames(Class c)
- {
- NSMutableArray * array = [NSMutableArray array];
- unsigned int methodCount = 0;
- Method * methodList = class_copyMethodList(c, &methodCount);
- unsigned int i;
- for(i = 0; i < methodCount; i++) {
- [array addObject: NSStringFromSelector(method_getName(methodList[i]))];
- }
- free(methodList);
- return array;
- }
- static void PrintDescription(NSString * name, id obj)
- {
- NSString * str = [NSString stringWithFormat:
- @"\n\t%@: %@\n\tNSObject class %s\n\tlibobjc class %s\n\timplements methods <%@>",
- name,
- obj,
- class_getName([obj class]),
- class_getName(obj->isa),
- [ClassMethodNames(obj->isa) componentsJoinedByString:@", "]];
- NSLog(@"%@", str);
- }
- - (void)testKVOImplementation
- {
- myPerson * anything = [[myPerson alloc] init];
- myPerson * hObserver = [[myPerson alloc] init];
- myPerson * wObserver = [[myPerson alloc] init];
- myPerson * hwObserver = [[myPerson alloc] init];
- myPerson * normal = [[myPerson alloc] init];
- [hObserver addObserver:anything forKeyPath:@"height" options:0 context:NULL];
- [wObserver addObserver:anything forKeyPath:@"weight" options:0 context:NULL];
- [hwObserver addObserver:anything forKeyPath:@"height" options:0 context:NULL];
- [hwObserver addObserver:anything forKeyPath:@"weight" options:0 context:NULL];
- PrintDescription(@"normal", normal);
- PrintDescription(@"hObserver", hObserver);
- PrintDescription(@"wObserver", wObserver);
- PrintDescription(@"hwOBserver", hwObserver);
- NSLog(@"\n\tUsing NSObject methods, normal setHeight: is %p, overridden setHeight: is %p\n",
- [normal methodForSelector:@selector(setHeight:)],
- [hObserver methodForSelector:@selector(setHeight:)]);
- NSLog(@"\n\tUsing libobjc functions, normal setHeight: is %p, overridden setHeight: is %p\n",
- method_getImplementation(class_getInstanceMethod(object_getClass(normal),
- @selector(setHeight:))),
- method_getImplementation(class_getInstanceMethod(object_getClass(hObserver),
- @selector(setHeight:))));
- }
2013-11-02 20:36:22.391 test[2438:c07]
normal: <myPerson: 0x886b840>
NSObject class myPerson
libobjc class myPerson
implements methods <weight, setWeight:, age, setAge:, height, setHeight:>
2013-11-02 20:36:22.393 test[2438:c07]
hObserver: <myPerson: 0x886b7e0>
NSObject class myPerson
libobjc class NSKVONotifying_myPerson
implements methods <setWeight:, setHeight:, class, dealloc, _isKVOA>
2013-11-02 20:36:22.393 test[2438:c07]
wObserver: <myPerson: 0x886b800>
NSObject class myPerson
libobjc class NSKVONotifying_myPerson
implements methods <setWeight:, setHeight:, class, dealloc, _isKVOA>
2013-11-02 20:36:22.393 test[2438:c07]
hwOBserver: <myPerson: 0x886b820>
NSObject class myPerson
libobjc class NSKVONotifying_myPerson
implements methods <setWeight:, setHeight:, class, dealloc, _isKVOA>
2013-11-02 20:36:22.394 test[2438:c07]
Using NSObject methods, normal setHeight: is 0x37e0, overridden setHeight: is 0x37e0
2013-11-02 20:36:22.394 test[2438:c07]
Using libobjc functions, normal setHeight: is 0x37e0, overridden setHeight: is 0xb859e0
从log信息可以清楚的看到派生了一个NSKVONotifying_XXX的类,这个派生类集合了每个KVO观察者的信息,所以这个派生类可以全局公用。
另外,观察原来类的方法和派生类的方法,每个被观察的属性都重写了,比如:setWeight:方法和setHeight:方法,没被观察的属性都没有重新生成,比如:height:方法、weight:方法、age:方法和setAge:方法。
0 0
- iOS下KVO的使用以及一些实现细节
- iOS下KVO的使用以及一些实现细节
- iOS KVO的一些理解和使用
- KVC/KVO 的使用以及实现原理
- ios KVO的使用和原理实现
- Ubuntu下使用GoAgent的一些细节
- iOS KVO的实现原理
- iOS中KVO的使用
- ios KVC KVO的使用
- ios KVO的简单使用
- GreenDao的使用以及一些细节的总结
- android studio使用的一些细节以及注意事项
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS Core Data的使用
- Length of Last Word
- JAVA JNA 调用 .so
- iOS中的KVC
- 第十一周 项目2--职员有薪水了(用深复制)
- iOS下KVO的使用以及一些实现细节
- 使用Eclipse构建Maven项目 (step-by-step)
- 【STL专项练习】热血格斗场
- Device Context 设备环境 设备上下文 理解
- 2700年前齐桓公玩弄的货币战争,今天的中国也在上演?!
- UML类图关系大全
- 【算法导论】【第三章】掌握渐进符号O和Ω
- 转: Redis消息队列的若干实现方式
- 苹果系统/mac系统垃圾桶不能清理干净的解决办法