之前从来不写文章,总感觉自己写的内容没什么价值。现在发现自己越不写越不知道该写些什么内容。从现在开始尝试写一些内容,锻炼自己。
昨天写了关于深浅拷贝的内容,今天来总结一下KVC和KVO。
######KVC(key - value - coding)键值编码
- 它提供了一种使用字符串而不是通过访问方法访问对象属性的一种机制。
- 通过
setValue: forKey:
的方式对类属性进行赋值操作,key作为属性名,value为属性对应的值。 - 通过
valueForKey:
访问对象的属性
可以通过以下简单的代码来尝试一下。 定义两个类,一个为Person
#import#import "Dog.h"@interface Person : NSObject { NSString *_sex; Dog *_dog;}@property (nonatomic, copy) NSString *name;//用来测试KVO对数组的效果@property (nonatomic, strong) NSMutableArray *dogs;- (void)showSex;- (void)showAge;- (void)showHeight;@end复制代码
#import "Person.h"@interface Person () { NSString *age;}@property (nonatomic, copy) NSString *height;@end@implementation Person- (instancetype)init { if (self = [super init]) { _dog = [[Dog alloc]init]; _dogs = [NSMutableArray new]; } return self;}- (void)showAge { NSLog(@"age : %@",age);}- (void)showSex { NSLog(@"sex : %@",_sex);}- (void)showHeight { NSLog(@"height : %@",_height);}复制代码
一个为Dog类
#import@interface Dog : NSObject@property (nonatomic, copy) NSString *name;@end复制代码
#import "Dog.h"@interface Dog () { NSString *_age; NSMutableDictionary *_undefinedKeyDict;}@property (nonatomic, copy) NSString *weight;@end@implementation Dog- (void)setWeight:(NSString *)weight { if (![_weight isEqualToString:weight]) { _weight = [weight copy]; }}//不做实现的话会崩溃- (void)setValue:(id)value forUndefinedKey:(NSString *)key { if (!_undefinedKeyDict) { _undefinedKeyDict = [NSMutableDictionary dictionary]; } [_undefinedKeyDict setObject:value forKey:key]; }- (id)valueForUndefinedKey:(NSString *)key { return [_undefinedKeyDict objectForKey:key];}@end复制代码
Person *p = [[Person alloc]init]; p.name = @"vayne"; NSLog(@"name : %@",p.name); [p setValue:@"famale" forKey:@"sex"]; [p showSex]; [p setValue:@"18" forKey:@"age"]; [p showAge]; [p setValue:@"185" forKey:@"height"]; [p showHeight]; Dog *d = [[Dog alloc]init]; d.name = @"huahua"; NSLog(@"dog name : %@",d.name); [d setValue:@"duoduo" forKey:@"name"]; NSLog(@"dog name : %@",d.name); [d setValue:@"30" forKey:@"weight"]; NSLog(@"weight : %@",[d valueForKey:@"weight"]); [d setValue:@"3" forKey:@"age"]; NSLog(@"age : %@",[d valueForKey:@"age"]); [p setValue:@"dog" forKeyPath:@"dog.name"]; NSLog(@"dog name : %@",[p valueForKeyPath:@"dog.name"]); //keyPath dog.xxxx并不存在,如果在dog类中不实现setValue: forUndefinedKey 会崩溃。 [p setValue:@"xxxx" forKeyPath:@"dog.xxxx"]; NSLog(@"xxxx : %@",[p valueForKeyPath:@"dog.xxxx"]); [d setValue:@"yyyy" forKey:@"yyyy"]; NSLog(@"yyyy : %@",[d valueForKey:@"yyyy"]); 复制代码通过上边的简单代码可以对KVC有个简单的了解,正因为这个特性,我们可以通过KVC来调用一些私有属性。正因为这样,KVO就是基于KVC实现的
######KVO(key - value - observed)键值观察 观察者模式(有时又被成为订阅模式/发布模式)是软件设计模式的一种。在此模式中,一个目标对象管理所有相依赖于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫观察者所提供的方法来实现。
通过- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
添加观察机制。其中observer
观察方法的持有者,keyPath
属性的名字,options
观察键值变化的几种可选方式,context
可以传NULL
;
接上边的代码,看一下KVO
[p addObserver:self forKeyPath:@"sex" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; [p setValue:@"male" forKey:@"sex"]; /* 如果被观察对象销毁,要移除其观察事件,否则会有崩溃。 */ [p removeObserver:self forKeyPath:@"sex"]; 复制代码
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context { NSLog(@"%@ %@ %@ %@ ",keyPath,[object class],change,context);}复制代码
当KVO作用于NSMutableArray时,观察并不会被调用,需要手动调用方法
//添加观察事件 [p addObserver:self forKeyPath:@"dogs" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil]; //需要手动调用 [p willChangeValueForKey:@"dogs"]; [p.dogs addObject:d]; //需要手动调用 [p didChangeValueForKey:@"dogs"]; [p removeObserver:self forKeyPath:@"dogs"];复制代码
最后在做一个简单的UI变化。 在屏幕上放置一个按钮,两个不同颜色的view
@property (weak, nonatomic) IBOutlet UIView *blueView;@property (weak, nonatomic) IBOutlet UIView *redView;复制代码
//观察redView的frame变化 [self.redView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"frameChanged"];//将blueView的frame赋值给redView- (IBAction)changeFrame:(id)sender { [UIView animateWithDuration:1 animations:^{ self.redView.frame = self.blueView.frame; }]; }- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context { NSLog(@"%@ %@ %@ %@ ",keyPath,[object class],change,context); //观察到redView的frame发生变化,之后将redView旧的值赋值给blueView,效果是两个view交换位置 [UIView animateWithDuration:1 animations:^{ self.blueView.frame = [[change objectForKey:@"old"]CGRectValue]; }]; }复制代码
KVO总结:
- 在对象销毁前,要取消观察事件,否则会crash
- 对被观察对象的数组属性,数组元素的改变无法调用观察事件,需要开发者手动操作。