KVO和KVC
一、KVC:键值编码
KVC的操作方法由NSKeyValueCoding协议提供,而NSObject实现了这个协议
基本作用:动态操作变量值
通过key/value字符串的方式访问变量,而不是直接使用变量名。
1、使用方法
1)获取值:valueForKey
NSString *n = [object valueForKey:@"name"]
2)设定值:
[object setValue:@"Daniel" forKey:@"name"]
ps:可针对的数据类型:对象,基本数据类型均可
2、键路径:key path
访问属性的属性时使用
访问方式:valueForKeyPath
[person valueForKeyPath:@"address.city"]
3、使一个类完全支持kvc
关键:1)传入的基本数据类型为nil时,重写默认方法,防止直接抛出异常
2)使用的key不存在时(传入,获取),重写默认方法,防止直接抛出异常
1)PersonInfo.h
#import <Foundation/Foundation.h> @interface PersonInfo : NSObject { NSString *name; float height; } //支持kvc,不用 @property 和 @synthesize的属性 -(NSString *)name; -(void)setName:(NSString *)name; -(float)height; -(void)setHeight:(float)height; //防止“基本数据类型”、“struct类型”传入nil后,默认方法直接抛异常 //当对非类对象属性设置nil时,调用,默认抛出异常 -(void)setNilValueForKey:(NSString *)key;//传入 //防止使用未知的key时,默认方法直接抛异常 -(void)setValue:(id)value forUndefinedKey:(NSString *)key;//传入 -(id)valueForUndefinedKey:(NSString *)key;//获取 @end
2)PersonInfo.m
#import "PersonInfo.h" @implementation PersonInfo -(NSString *)name{ return name; } -(float)height{ return height; } - (void)setNilValueForKey:(NSString *)key//传入 { NSLog(@"传入nil值"); if ([key isEqualToString:@"height"]){ [self setValue:@"99.00" forKey:key]; } else [super setNilValueForKey:key]; } -(void)setValue:(id)value forUndefinedKey:(NSString *)key//传入 { NSLog(@"未定义的key,无法设置key-value"); } -(id)valueForUndefinedKey:(NSString *)key;//获取 { NSLog(@"未知的key"); return @"未知的key"; } @end
3)调用:
PersonInfo *personInfo=[[PersonInfo alloc]init]; [personInfo setValue:nil forKey:@"height"];//kvc的方式设置某个属性为nil [personInfo setValue:@1.78 forKey:@"heightsss"];//kvc的方式设置不存在的key [personInfo valueForKey:@"heisght"];//kvc的方式获取不存在的key //正常存入,获取 NSLog(@"personInfo.height:%@",[personInfo valueForKey:@"height"]); [personInfo setValue:@1.88 forKey:@"height"]; NSLog(@"personInfo.height新值:%@",[personInfo valueForKey:@"height"]);
4)运行结果:
4、实用场景:
根据集合中存储的“属性名”,通过字符串拼接动态获取“ui元素名”,实现动态操作ui元素的功能
实现:略
参考:http://blog.jobbole.com/69731/
5、补充:对于3个方法比较通用的实现
- (void)setNilValueForKey:(NSString *)key//传入 { NSLog(@"当前key为:%@,传入nil值",key); [super setNilValueForKey:key]; } -(void)setValue:(id)value forUndefinedKey:(NSString *)key//传入 { NSLog(@"未定义的key,无法设置key-value"); } -(id)valueForUndefinedKey:(NSString *)key;//获取 { NSLog(@"未定义的key,无法获取"); return @"未定义的key,无法获取"; }
二、KVO:键值监听
重要特点:使用时“添加”和“移除”必须成对出现
1、基本概念: 1)一种观察者模式
2)需要实现NSKeyValueObServing协议(NSObject中已实现)
2、使用方法:1)“被监听对象”通过addObserver: forKeyPath: options: context:注册监听
2)实现监听回调方法:observeValueForKeyPath: ofObject: change: context:
3)覆盖dealloc方法,移除监听:removeObserver: forKeyPath: context:
3、线程安全性:1)值的改变和监听发生在同一个线程上
2)如果要从“子线程”中改变属性值,需要保证:
所有的监听者(通常是操作UI对象)都用线程安全的方式处理KVO通知
ps:performSelectorOnMainThread??
4、代码实现:
1)被监听者
StockInfo.h:
#import <Foundation/Foundation.h> @interface StockInfo : NSObject @property(nonatomic,copy)NSString *stockName; @property(nonatomic,assign)float stockPrice; @end
StockInfo.m:
#import "StockInfo.h" @implementation StockInfo @synthesize stockName=_stockName; @synthesize stockPrice=_stockPrice; @end
2)监听者(操作UI的对象)
UI_Observer.h:
#import <UIKit/UIKit.h> @interface UI_Observer : UIViewController @end
UI_Observer.m:
#import "UI_Observer.h" #import "StockInfo.h" @interface UI_Observer () @property(nonatomic,strong)StockInfo *stockInfo; @property(nonatomic,strong)UILabel *lblPrice; @property(nonatomic,strong)UIButton *btnChangePrice; @end @implementation UI_Observer - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor=[UIColor yellowColor]; //为stockInfo添加观察者 self.stockInfo=[[StockInfo alloc]init]; //self.stockInfo.stockName=@"stockOO1"; //self.stockInfo.stockPrice=@"17.2"; [self.stockInfo setValue:@"stockOO1" forKey:@"stockName"]; [self.stockInfo setValue:@"10.0" forKey:@"stockPrice"]; [self.stockInfo addObserver:self forKeyPath:@"stockPrice" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; //初始化界面元素 self.lblPrice = [[UILabel alloc]initWithFrame:CGRectMake(self.view.frame.size.width/2-50, 100, 100, 30 )]; self.lblPrice.textColor = [UIColor redColor]; self.lblPrice.text = [[self.stockInfo valueForKey:@"stockPrice"] stringValue];//必须加上stringValue的类型强转 [self.view addSubview:self.lblPrice]; self.btnChangePrice = [UIButton buttonWithType:UIButtonTypeRoundedRect]; self.btnChangePrice.frame = CGRectMake(self.view.frame.size.width/2-50, 300, 100, 30); self.btnChangePrice.backgroundColor=[UIColor whiteColor]; [self.btnChangePrice setTitle:@"改变价格" forState:UIControlStateNormal]; [self.btnChangePrice addTarget:self action:@selector(btnChangePriceClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:self.btnChangePrice]; } -(void)btnChangePriceClick{ //self.stockInfo.stockPrice=@"29.9"; //值改变后,回调方法被自动触发 [self.stockInfo setValue:@"29.9" forKey:@"stockPrice"]; } #pragma mark 监听的回调方法:observeValueForKeyPath,价格变化后通知ui改变 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if([keyPath isEqualToString:@"stockPrice"]){//这里只处理stockPrice属性 NSLog(@"keyPath: %@",keyPath); NSLog(@"object: %@",object); NSLog(@"更新前的旧值: %.2f",[[change objectForKey:@"old"] floatValue]); NSLog(@"更新后的新值: %.2f",[[change objectForKey:@"new"] floatValue]); NSLog(@"context: %@",context); self.lblPrice.text = [[self.stockInfo valueForKey:@"stockPrice"] stringValue];//必须加上stringValue的类型强转 } } //重写销毁方法 -(void)dealloc{ [self.stockInfo removeObserver:self forKeyPath:@"stockPrice"];//移除监听 //[super dealloc];//启用了ARC,此处不需要调用 } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
3)运行结果:
点击按钮:
深入理解:http://blog.jobbole.com/69731/
参考:http://www.cnblogs.com/kenshincui/p/3871178.html
浙公网安备 33010602011771号