《key-value observing (键值监测)》由会员分享,可在线阅读,更多相关《key-value observing (键值监测)(16页珍藏版)》请在金锄头文库上搜索。
1、Key-Value Observing (键值监测)简介KVO 是一套当目标对象的属性值改变时观察者对象能够接受到通知的机制。必须先理解KVC 才能更好的理解 KVO,前者是后者的实现基础。 这样的通信机制在 MVC 设计模式很是常见实现过程简单来说分为 3 步: 1、添加观察这和监测对象 2、监测对象改变 3、收到值改变通知,处理后续逻辑 举个生活中的例子就是给银行卡开通短信通知的业务,总体也是分 3 步“ 1、去银行办理短信业务 2、账号财产变动 3、收到短信通知 KVO 是框架级别的服务,无需自己发送通知,使用方便,基本不需要添加额外代码即可使用。详情为了使用 KVO,必须满足以下 3
2、步1、目标对象的属性,必须支持 KVO2、注册观察者与被观察者 addObserver:forKeyPath:options:context:3、观察者必须实现 observeValueForKeyPath:ofObject:change:context:方法第一步、确保目标支持 KVO被监测的目标对象的属性支持 KVO 必须满足以下条件:1、目标对象的属性必须支持 KVC,对于 1 对 1 属性简单来说就是实现 set 和 get 方法。详情和 1 对多请阅读官方说明。系统已有类及子类自动支持,放心使用。2、自动和手动属性通知 目标对象必须能发出属性变化通知。系统默认支持,也可自定义。 系统
3、默认支持,且支持的很好,一般无需自定义。/如果需要自定义,需要重新此方法,默认返回 YES+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;/在 set 方法中手动调用,变化类型只是针对 NSKeyValueChangeSetting- (void)willChangeValueForKey:(NSString *)key;- (void)didChangeValueForKey:(NSString *)key;例如/假设有属性property (nonatomic,copy)NSString *name;+ (BOOL)
4、automaticallyNotifiesObserversForKey:(NSString *)theKey BOOL automatic = NO;/* 只自定义指定属性,其它仍然自动发送通知 */if (theKey isEqualToString:name)/在 set 方法中手动调用相关方法automatic = NO;else/此方法默认返回 YESautomatic = super automaticallyNotifiesObserversForKey:theKey;return automatic;- (void)setName:(NSString *)name/即将变化se
5、lf willChangeValueForKey:name;_name = name; /已经变化self didChangeValueForKey:name;/如果说只有值不相等时才发送通知,提升性能- (void)setName:(NSString *)nameif (!name isEqualToString:_name)self willChangeValueForKey:name;_name = name;self didChangeValueForKey:name;如果涉及 1 对多的容器类,需要自己实现 NSKeyValueChangeInsertion, NSKeyValueC
6、hangeRemoval, NSKeyValueChangeReplacement 三种操作对应的方法,例如/Keys 为属性名称- (void)removeKeysAtIndexes:(NSIndexSet *)indexes self willChange:NSKeyValueChangeRemovalvaluesAtIndexes:indexes forKey:keys;/ Remove the transaction objects at the specified indexes.self didChange:NSKeyValueChangeRemovalvaluesAtIndexe
7、s:indexes forKey:keys;3、属性依赖 如果目标对象属性存在依赖关系,注册合适的依赖 Keys。核心方法为第一种、+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key NS_AVAILABLE(10_5, 2_0);说明:1、返回目标属性依赖属性的 KeyPath 的 Set。当对象注册后,KVO 自动监测该对象所有的KeyPaths。2、其默认实现从对象所属类的方法列表中匹配方法:+keyPathsForValuesAffecting(为属性名,比如 Name) ,如果存在执行并返回结果;如果不存在
8、向底层寻找已经废弃的方法+setKeys:triggerChangeNotificationsForDependentKey:3、可以用来替换手动调用-willChangeValueForKey:/-didChangeValueForKey:来实现属性依赖的解决方案4、不能在已有类的 Category 中使用,在 Category 禁止重写此方法,可以使用+keyPathsForValuesAffecting来实现。第二种、或者重写此格式+keyPathsForValuesAffecting(为属性名,比如 Name)的方法名比如说,姓名=姓+ 名;当二者任一变动时更新姓名property (
9、nonatomic,copy)NSString *name;/姓名property (nonatomic,copy)NSString *firstName;/姓property (nonatomic,copy)NSString *lastName;/名/重新 get 方法,表明字段组成-(NSString *)namereturn NSString stringWithFormat:%,_firstName,_lastName;/通过此方法+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key NSSet *keyPat
10、hs = super keyPathsForValuesAffectingValueForKey:key;if (key isEqualToString:name)NSArray *affectingKeys = lastName, firstName;keyPaths = keyPaths setByAddingObjectsFromArray:affectingKeys;return keyPaths;或者+ (NSSet *)keyPathsForValuesAffectingNamereturn NSSet setWithObjects:lastName, firstName, nil
11、;/改变值- (void)viewDidLoad super viewDidLoad;self setValue:张 forKey:firstName;self setValue:三 forKey:lastName;self addObserver:self forKeyPath:name options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil;/名称改变,刷新姓名self setValue:龙 forKey:lastName;-(void)observeValueForKeyPath:(NSS
12、tring *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)contextNSLog(NSKeyValueChangeOldKey:%,changeNSKeyValueChangeOldKey);NSLog(NSKeyValueChangeNewKey:%,changeNSKeyValueChangeNewKey);/输出结果2016-09-06 17:05:01.904 KVC4192:307124 NSKeyValueChangeOldKey:张三2016-09-06 17:05:01.
13、904 KVC4192:307124 NSKeyValueChangeNewKey:张龙注意:以上关于属性依赖的处理方法不支持一对多的关系。比如说 ViewController 对象有一个 totalNumber 表示总数和属性 datas 数组,数组中 Data 的对象,对象含有 number 属性。/* Data 对象 */interface Data : NSObjectproperty (nonatomic,assign)NSInteger number;end/* ViewController 对象 */interface ViewController ()property (non
14、atomic,assign)NSInteger totalNumber;property (nonatomic,strong)NSArray *datas;end可以采用以下方法解决 1、注册每个 Data 对象的年龄属性为监测属性,ViewController 对象为观察者,data.number变化时,使用集合运算符求和,然后设置 ViewController 的 totalNumber 属性值- (void)viewDidLoad super viewDidLoad;/* 创建 Data 对象 */Data * data1 = Data alloc init;data1.number =
15、 0;Data *data2 = Data alloc init;data2.number = 0;Data *data3 = Data alloc init;data3.number = 0;/* self.datas 属性赋值 */self setValue:data1,data2,data3 forKey:datas;/* 监测 self.datas 中每个 data 对象的 number 属性 */(0, 3) 中 0 表示 index 从 0 开始,0 表示长度 3,也就是 index(0、1、2) ;但是不能使得 self.datas 数组越界NSIndexSet *set = NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3);self.datas addObserver:self toObjectsAtIndexes:set forKeyPath:number options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil;/* 监测 totalNumber 属性 */self addObserver:self forKeyPath: