码迷,mamicode.com
首页 > 其他好文 > 详细

【OC基础】04-OC中的KVC、KVO

时间:2015-05-04 20:08:32      阅读:144      评论:0      收藏:0      [点我收藏+]

标签:

概述

在Java和C#中都提供了反射的功能,既根据字符串能动态的创建对象并修改对象的属性。OC内置了这些功能,使得我们在操作的时候更方便。

键值编码KVC

kvc是Key for Value的缩写,KVC的操作方法由NSKeyValueCoding协议提供,而NSObject就实现了这个协议,也就是说ObjC中几乎所有的对象都支持KVC操作,常用的KVC操作方法如下:

1>动态设置: setValue:属性值 forKey:属性名(用于简单路径)setValue:属性值 forKeyPath:属性路径(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性)

2>动态读取: valueForKey:属性名 valueForKeyPath:属性名(用于复合路径)

例子如下:

Book.h

1 #import <Foundation/Foundation.h>
2 
3 @interface Book : NSObject
4 @property (nonatomic,copy) NSString* bookName;
5 @property (nonatomic,assign)float price;
6 @end

Book.m

1 #import "Book.h"
2 
3 @implementation Book
4 
5 @end

Student.h

 1 #import <Foundation/Foundation.h>
 2 #import "Book.h"
 3 @interface Student : NSObject
 4 
 5 #pragma mark - 属性
 6 @property (nonatomic,assign)int age;
 7 @property (nonatomic,copy) NSString *name;
 8 @property (nonatomic,retain) Book *book;
 9 -(void)show;
10 @end

Student.m

1 #import "Student.h"
2 
3 @implementation Student
4 -(void)show{
5     NSLog(@"name=%@ and age=%d",_name,_age);
6 }
7 @end

main.m

 1 #import <Foundation/Foundation.h>
 2 #import "Student.h"
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         Student *stu1=[[Student alloc]init];
 8         [stu1 setValue:@"lisi" forKey:@"name"];
 9         [stu1 setValue:@14 forKey:@"age"];
10         [stu1 show];
11         
12         NSLog(@"stu1 name=%@,age=%@",[stu1 valueForKey:@"name"],[stu1 valueForKey:@"age"]);
13         
14         Book *book1=[[Book alloc]init];
15         stu1.book=book1;
16         [stu1 setValue:@"99.8" forKeyPath:@"book.price"];
17         [stu1 setValue:@"mathBook" forKeyPath:@"book.bookName"];
18         NSLog(@"stu1 bookname=%@ price=%@",[stu1 valueForKeyPath:@"book.bookName"],[stu1 valueForKeyPath:@"book.price"]);
19         
20     }
21     return 0;
22 }

KVC是如何对属性进行读取的呢?假如要读取属性a

  • 如果是动态设置属性,则优先考虑调用setA方法,如果没有该方法则优先考虑搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的setValue:forUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确设置);
  • 如果是动态读取属性,则优先考虑调用a方法(属性a的get方法),如果没有搜索到则会优先搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的valueforUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确读取);

键值监听KVO

在C#或者Java图形界面编程的时候,我们改变控件的某个属性,一般都能动态的反应到UI的变化上。这实际上利用的观察者模式,利用这种模式很容易实现数据模型和界面操作的分离。OC默认内置了这项功能叫KVO。在OC中要实现KVO则必须实现NSKeyValueObServing协议,不过NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO。

在OC中使用KVO操作常用的方法如下:

1>注册监听: addObserver: forKeyPath: options:  context:

2>回调监听 observeValueForKeyPath: ofObject: change: context:

3>删除监听: removeObserver: forKeyPathremoveObserver: forKeyPath: context:

 

KVO的使用步骤也比较简单:

1>通过addObserver: forKeyPath: options: context:为被监听对象(它通常是数据模型)注册监听器

2>重写监听器的observeValueForKeyPath: ofObject: change: context:方法

上面的例子中,假如我们希望在Book价格改变的时候通知Student,我们可以这样写:

Student.h:

 1 #import <Foundation/Foundation.h>
 2 #import "Book.h"
 3 @interface Student : NSObject
 4 
 5 #pragma mark - 属性
 6 @property (nonatomic,assign)int age;
 7 @property (nonatomic,copy) NSString *name;
 8 @property (nonatomic,retain) Book *book;
 9 -(void)show;
10 @end

Student.m:

 1 #import "Student.h"
 2 
 3 @implementation Student
 4 -(void)show{
 5     NSLog(@"name=%@ and age=%d",_name,_age);
 6 }
 7 
 8 -(void)setBook:(Book *)book{
 9     _book=book;
10     //添加对Book的监听
11     [self.book addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew context:nil];
12 }
13 
14 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
15     //Book价格变化会调用这个函数
16     if([keyPath isEqualToString:@"price"]){
17         NSLog(@"keyPath=%@,Obj=%@,newValue=%f,context=%@",keyPath,object,[[change objectForKey:@"new"]floatValue],context);
18     }
19 }
20 -(void)dealloc{
21     //移除监听
22     [self.book removeObserver:self forKeyPath:@"price"];
23     [super dealloc];
24 }
25 @end

main.m:

 1 #import <Foundation/Foundation.h>
 2 #import "Student.h"
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         Student *stu1=[[Student alloc]init];
 8         [stu1 setValue:@"lisi" forKey:@"name"];
 9         [stu1 setValue:@14 forKey:@"age"];
10         [stu1 show];
11         
12         NSLog(@"stu1 name=%@,age=%@",[stu1 valueForKey:@"name"],[stu1 valueForKey:@"age"]);
13         
14         Book *book1=[[Book alloc]init];
15         stu1.book=book1;
16         [stu1 setValue:@"99.8" forKeyPath:@"book.price"];
17         [stu1 setValue:@"mathBook" forKeyPath:@"book.bookName"];
18         NSLog(@"stu1 bookname=%@ price=%@",[stu1 valueForKeyPath:@"book.bookName"],[stu1 valueForKeyPath:@"book.price"]);
19         
20     }
21     return 0;
22 }

结果输出:

1 2015-05-04 11:54:54.557 first[2697:303] name=lisi and age=14
2 2015-05-04 11:54:54.559 first[2697:303] stu1 name=lisi,age=14
3 2015-05-04 11:54:54.560 first[2697:303] keyPath=price,Obj=<Book: 0x1024004a0>,newValue=99.800003,context=(null)
4 2015-05-04 11:54:54.560 first[2697:303] stu1 bookname=mathBook price=99.8

我们给Book的price属性注册了监听,并添加了监听回调方法,这样在我们改变Book的price属性时,就会调用我们的回调方法打印出相关的信息。通过这个例子我们可以看出,KVO的本质是:

1>在要监听的成员中的get/set方法中注册回调函数

2>重写这个回调函数

这样在改变这个属性的时候就会自动调用我们的回调函数,实现了观察者模式。

 

【OC基础】04-OC中的KVC、KVO

标签:

原文地址:http://www.cnblogs.com/desheng-Win/p/4477005.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!