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


    概述

    在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>重写这个回调函数

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

  • 相关阅读:
    strtok和strtok_r
    几种更新(Update语句)查询的方法
    常见的几种RuntimeException
    初识ASP.NET---若干常见错误
    Microsoft.AlphaImageLoader滤镜解说
    情绪管理--不要总做“好脾气”的人。
    Linux中搭建SVNserver
    Java虚拟机的启动与程序的执行
    Ubuntu下deb包的安装方法
    財哥面京东dm的经历【帮財哥发的】
  • 原文地址:https://www.cnblogs.com/desheng-Win/p/4477005.html
Copyright © 2020-2023  润新知