Key-Value Observing (键值监测).doc_第1页
Key-Value Observing (键值监测).doc_第2页
Key-Value Observing (键值监测).doc_第3页
Key-Value Observing (键值监测).doc_第4页
Key-Value Observing (键值监测).doc_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Key-Value Observing (键值监测)简介KVO是一套当目标对象的属性值改变时观察者对象能够接受到通知的机制。必须先理解KVC才能更好的理解KVO,前者是后者的实现基础。 这样的通信机制在MVC设计模式很是常见实现过程简单来说分为3步: 1、添加观察这和监测对象 2、监测对象改变 3、收到值改变通知,处理后续逻辑 举个生活中的例子就是给银行卡开通短信通知的业务,总体也是分3步“ 1、去银行办理短信业务 2、账号财产变动 3、收到短信通知 KVO是框架级别的服务,无需自己发送通知,使用方便,基本不需要添加额外代码即可使用。详情为了使用KVO,必须满足以下3步1、目标对象的属性,必须支持KVO2、注册观察者与被观察者addObserver:forKeyPath:options:context:3、观察者必须实现observeValueForKeyPath:ofObject:change:context:方法第一步、确保目标支持KVO被监测的目标对象的属性支持KVO必须满足以下条件:1、目标对象的属性必须支持KVC,对于1对1属性简单来说就是实现set和get方法。详情和1对多请阅读官方说明。系统已有类及子类自动支持,放心使用。2、自动和手动属性通知 目标对象必须能发出属性变化通知。系统默认支持,也可自定义。 系统默认支持,且支持的很好,一般无需自定义。/如果需要自定义,需要重新此方法,默认返回YES+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;/在set方法中手动调用,变化类型只是针对NSKeyValueChangeSetting- (void)willChangeValueForKey:(NSString *)key;- (void)didChangeValueForKey:(NSString *)key;例如/假设有属性property (nonatomic,copy)NSString *name;+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey BOOL automatic = NO; /* 只自定义指定属性,其它仍然自动发送通知 */ if (theKey isEqualToString:name) /在set方法中手动调用相关方法 automatic = NO; else /此方法默认返回YES automatic = super automaticallyNotifiesObserversForKey:theKey; return automatic;- (void)setName:(NSString *)name /即将变化 self willChangeValueForKey:name; _name = name; /已经变化 self didChangeValueForKey:name;/如果说只有值不相等时才发送通知,提升性能- (void)setName:(NSString *)name if (!name isEqualToString:_name) self willChangeValueForKey:name; _name = name; self didChangeValueForKey:name; 如果涉及1对多的容器类,需要自己实现 NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, NSKeyValueChangeReplacement三种操作对应的方法,例如/Keys为属性名称- (void)removeKeysAtIndexes:(NSIndexSet *)indexes self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:keys; / Remove the transaction objects at the specified indexes. self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:keys;3、属性依赖 如果目标对象属性存在依赖关系,注册合适的依赖Keys。核心方法为第一种、+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key NS_AVAILABLE(10_5, 2_0);说明:1、返回目标属性依赖属性的KeyPath的Set。当对象注册后,KVO自动监测该对象所有的KeyPaths。2、其默认实现从对象所属类的方法列表中匹配方法:+keyPathsForValuesAffecting(为属性名,比如Name),如果存在执行并返回结果;如果不存在向底层寻找已经废弃的方法+setKeys:triggerChangeNotificationsForDependentKey:3、可以用来替换手动调用-willChangeValueForKey:/-didChangeValueForKey:来实现属性依赖的解决方案4、不能在已有类的Category中使用,在Category禁止重写此方法,可以使用+keyPathsForValuesAffecting来实现。第二种、或者重写此格式+keyPathsForValuesAffecting(为属性名,比如Name)的方法名比如说,姓名=姓+名;当二者任一变动时更新姓名property (nonatomic,copy)NSString *name;/姓名property (nonatomic,copy)NSString *firstName;/姓property (nonatomic,copy)NSString *lastName;/名/重新get方法,表明字段组成-(NSString *)name return NSString stringWithFormat:%,_firstName,_lastName;/通过此方法+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key NSSet *keyPaths = super keyPathsForValuesAffectingValueForKey:key; if (key isEqualToString:name) NSArray *affectingKeys = lastName, firstName; keyPaths = keyPaths setByAddingObjectsFromArray:affectingKeys; return keyPaths;或者+ (NSSet *)keyPathsForValuesAffectingName return NSSet setWithObjects:lastName, firstName, nil;/改变值- (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:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context NSLog(NSKeyValueChangeOldKey:%,changeNSKeyValueChangeOldKey); NSLog(NSKeyValueChangeNewKey:%,changeNSKeyValueChangeNewKey);/输出结果2016-09-06 17:05:01.904 KVC4192:307124 NSKeyValueChangeOldKey:张三2016-09-06 17:05:01.904 KVC4192:307124 NSKeyValueChangeNewKey:张龙注意:以上关于属性依赖的处理方法不支持一对多的关系。比如说ViewController对象有一个totalNumber表示总数和属性datas数组,数组中Data的对象,对象含有number属性。/* Data对象 */interface Data : NSObjectproperty (nonatomic,assign)NSInteger number;end/* ViewController对象 */interface ViewController ()property (nonatomic,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 = 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:totalNumber options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil; /* 改变值,查看输出结果 */ data1 setValue:1 forKey:number; data2 setValue:1 forKey:number; data3 setValue:1 forKey:number;-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context if (keyPath isEqualToString:number) self updateTotalNumber; else if (keyPath isEqualToString:totalNumber) NSLog(%-%,keyPath,change); /更新总数- (void)updateTotalNumber NSNumber *total = self valueForKeyPath:datas.sum.number; self setValue:total forKey:totalNumber;/输出结果2016-09-07 14:10:10.694 KVC3034:165515 totalNumber- kind = 1; new = 1; old = 0;2016-09-07 14:10:10.695 KVC3034:165515 totalNumber- kind = 1; new = 2; old = 1;2016-09-07 14:10:10.695 KVC3034:165515 totalNumber- kind = 1; new = 3; old = 2;以上代码中不涉及移除,根据需要添加代码,对象delloc之前,必须移除。 2、Core Data,自动实现类似的功能。第二步、注册1、注册通知 为了能够获取目标属性值改变的通知,需要注册观察者和观察对象属性- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;参数说明:observer:观察者对象,就是想收到变动通知的对象keyPath:监测的目标属性的路径options:决定了通知中内容和发送时间context:C指针或者对象,传递参数,一般不用传NULL例如在新建的工程的ViewController中property (nonatomic,copy)NSString *name;- (void)registerAsObserver self addObserver:self forKeyPath:name options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil;注意:此方法不持有观察者对象、被观察对象、context,管理好其生命周期。2、接受通知 当监测的目标对象的属性变化时,观察者将调用observeValueForKeyPath:ofObject:change:context: message,所有的观察者都必须实现此方法- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context;keyPath:监测的目标属性的路径object:观察者对象change:变化内容context:C指针或者对象,传递参数,一般不用传NULL3、移除通知 当不再使用时,需要通过以下方法移除通知。- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;keyPath:监测的目标属性的路径observer:观察者对象context:C指针或者对象,传递参数,一般不用传NULL以上两个方法,根据需要选择使用。特别注意:NSArray、NSOrderedSet、NSSet不支持以上三个方法,调用会抛出异常。第三步、属性变化使用KVC方法,或者能够触发KVC方法使得监测的目标对象属性变化。第四步、接收变化当监测的目标对象的属性变化时,观察者将调用observeValueForKeyPath:ofObject:change:context: message,所有的观察者都必须实现此方法。在此方法中处理变化以上第二、三、四步组成一次完整的KVO使用过程,下边关于一些参数的用法说明参数说明关于NSKeyValueObservingOptions- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;options:决定了通知中内容和发送时间12NSKeyValueObservingOptions是一个枚举类型typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) /*通知dic中是否包含新值*/ NSKeyValueObservingOptionNew = 0x01, /*通知dic中是否包含新值*/ NSKeyValueObservingOptionOld = 0x02, /*添加此操作,通知dic中是否包含注册通知前的初始值;如果目标属性是容器类,每个元素都会触发通知发送*/ NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04, /*添加此操作,每次值变化,将触发两次:1、变化前(dic中包含NSKeyValueChangeNotificationIsPriorKey,值为1,NSNumber类型) 2、变化后 */ NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08;关于Change Dictionary- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDnary *)change context:(nullable void *)context;change:变化内容12其包含以下几种内容,可以使用以下字段取值/值变化类型FOUNDATION_EXPORT NSString *const NSKeyValueChangeKindKey;/新值FOUNDATION_EXPORT NSString *const NSKeyValueChangeNewKey;/旧值FOUNDATION_EXPORT NSString *const NSKeyValueChangeOldKey;/容器类中,变化值所在位置,NSIndexSet类型FOUNDATION_EXPORT NSString *const NSKeyValueChangeIndexesKey;/是否值变化前,NSNumber类型FOUNDATION_EXPORT NSString *const NSKeyValueChangeNotificationIsPriorKey NS_AVAILABLE(10_5, 2_0);其中NSKeyValueChangeKindKey有以下几种类型typedef NS_ENUM(NSUInteger, NSKeyValueChange) NSKeyValueChangeSetting = 1,/值变化 NSKeyValueChangeInsertion = 2,/插入 NSKeyValueChangeRemoval = 3,/移除 NSKeyValueChangeReplacement = 4,/替换;上边的NSKeyValueChangeKindKey2、3、4分别对应着有序集合比如NSArray中增、删、改操作。参数说明-代码示例1、NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldproperty (nonatomic,copy)NSString *name;/注册- (void)viewDidLoad super viewDidLoad; self setValue:zwq forKey:name; self addObserver:self forKeyPath:name options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil; self setValue:zwq2 forKey:name;/接收-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context NSLog(NSKeyValueChangeKindKey:%,changeNSKeyValueChangeKindKey); NSLog(NSKeyValueChangeOldKey:%,changeNSKeyValueChangeOldKey); NSLog(NSKeyValueChangeNewKey:%,changeNSKeyValueChangeNewKey); NSLog(change dic:%,change);/输出结果2016-09-06 14:58:55.349 KVC3614:231751 NSKeyValueChangeKindKey:12016-09-06 14:58:55.351 KVC3614:231751 NSKeyValueChangeOldKey:zwq2016-09-06 14:58:55.351 KVC3614:231751 NSKeyValueChangeNewKey:zwq22016-09-06 14:58:55.352 KVC3614:231751 change dic: kind = 1; new = zwq2; old = zwq;从以上代码可以看出change中的key如何取值,以及其中内容。2、NSKeyValueObservingOptionInitialproperty (nonatomic,copy)NSString *name;/注册- (void)viewDidLoad super viewDidLoad; self setValue:zwq forKey:name; /为了明显观察值变化,多添加两个key self addObserver:self forKeyPath:name options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil; self setValue:zwq2 forKey:name;/接收-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context NSLog(NSKeyValueChangeKindKey:%,changeNSKeyValueChangeKindKey); NSLog(NSKeyValueChangeOldKey:%,changeNSKeyValueChangeOldKey); NSLog(NSKeyValueChangeNewKey:%,changeNSKeyValueChangeNewKey); NSLog(change dic:%,change);/输出结果2016-09-06 15:06:49.963 KVC3654:237675 NSKeyValueChangeKindKey:12016-09-06 15:06:49.964 KVC3654:237675 NSKeyValueChangeOldKey:(null)2016-09-06 15:06:49.964 KVC3654:237675 NSKeyValueChangeNewKey:zwq2016-09-06 15:06:49.964 KVC3654:237675 change dic: kind = 1; new = zwq;2016-09-06 15:06:49.964 KVC3654:237675 NSKeyValueChangeKindKey:12016-09-06 15:06:49.965 KVC3654:237675 NSKeyValueChangeOldKey:zwq2016-09-06 15:06:49.965 KVC3654:237675 NSKeyValueChangeNewKey:zwq22016-09-06 15:06:49.965 KVC3654:237675 change dic: kind = 1; new = zwq2; old = zwq;对比1、2的输出结果,可以看出2中可以获得初始值,后续值变化接收到的通知dic内容同1中一样(可以多改变几次赋值,观察结果)3、NSKeyValueObservingOptionPriorproperty (nonatomic,copy)NSString *name;/注册- (void)viewDidLoad super viewDidLoad; self setValue:zwq forKey:name; /为了明显观察值变化,多添加两个key self addObserver:self forKeyPath:name options:NSKeyValueObservingOptionPrior|NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil; self setValue:zwq2 forKey:name; self setValue:zwq3 forKey:name;/接收-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context NSLog(NSKeyValueChangeKindKey:%,changeNSKeyValueChangeKindKey); NSLog(NSKeyValueChangeOldKey:%,changeNSKeyValueChangeOldKey); NSLog(NSKeyValueChangeNewKey:%,changeNSKeyValueChangeNewKey); NSLog(NSKeyValueChangeNotificationIsPriorKey:%,changeNSKeyValueChangeNotificationIsPriorKey); NSLog(change dic:%,change);/输出结果2016-09-06 15:17:16.325 KVC3730:246941 NSKeyValueChangeKindKey:12016-09-06 15:17:16.326 KVC3730:246941 NSKeyValueChangeOldKey:zwq2016-09-06 15:17:16.326 KVC3730:246941 NSKeyValueChangeNewKey:(null)2016-09-06 15:17:16.326 KVC3730:246941 NSKeyValueChangeNotificationIsPriorKey:12016-09-06 15:17:16.327 KVC3730:246941 change dic: kind = 1; notificationIsPrior = 1; old = zwq;2016-09-06 15:17:16.327 KVC3730:246941 NSKeyValueChangeKindKey:12016-09-06 15:17:16.327 KVC3730:246941 NSKeyValueChangeOldKey:zwq2016-09-06 15:17:16.327 KVC3730:246941 NSKeyValueChangeNewKey:zwq22016-09-06 15:17:16.327 KVC3730:246941 NSKeyValueChangeNotificationIsPriorKey:(null)2016-09-06 15:17:16.328 KVC3730:246941 change dic: kind = 1; new = zwq2; old = zwq;2016-09-06 15:17:16.328 KVC3730:246941 NSKeyValueChangeKindKey:12016-09-06 15:17:16.328 KVC3730:246941 NSKeyValueChangeOldKey:zwq22016-09-06 15:17:16.328 KVC3730:246941 NSKeyValueChangeNewKey:(null)2016-09-06 15:17:16.328 KVC3730:246941 NSKeyValueChangeNotificationIsPriorKey:12016-09-06 15:17:16.328 KVC3730:246941 change dic: kind = 1; notificationIsPrior = 1; old = zwq2;2016-09-06 15:17:16.328 KVC3730:246941 NSKeyValueChangeKindKey:12016-09-06 15:17:16.329 KVC3730:246941 NSKeyValueChangeOldKey:zwq22016-09-06 15:17:16.329 KVC3730:246941 NSKeyValueChangeNewKey:zwq32016-09-06 15:17:16.329 KVC3730:246941 NSKeyValueChangeNotificationIsPriorKey:(null)2016-09-06 15:17:16.329 KVC3730:246941 change

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论