zoukankan      html  css  js  c++  java
  • [Objective-C] 021 KVC、KVO

    写过C#的都知道C#通过反射读写一个对象的属性特别方便,可以利用字符串的方式去动态控制一个对象。其实在ObjC中,我们可以更高级点,根本不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Value Coding(简称KVC)。

    KVC(键值编码)

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

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

    我们通过一个简单的例子来说明

    Blog 类

    //////////////////////// Blog.h ////////////////////  
    #import <Foundation/Foundation.h>
    
    @interface Blog : NSObject
    
    //文章数 
    @property(nonatomic,assign)int essayCount;
    @end
    
    /////////////////////// Blog.m ///////////////////
    #import "Blog.h"
    
    @implementation Blog
    
    @end
    

    User 类

    ////////////////////// User.h /////////////////////
    #import <Foundation/Foundation.h>
    #import "Blog.h"
    
    @interface User : NSObject
    
    @property(nonatomic,copy)NSString *name;
    @property(nonatomic,assign)int age;
    @property(nonatomic,retain)Blog *blog;
    
    - (void)showUserInfo;
    @end
    
    //////////////////// User.m /////////////////////
    #import "User.h"
    
    @implementation User
    
    - (void)showUserInfo {
        NSLog(@"userName=%@,userAge=%d,essayCount=%d",_name,_age,_blog.essayCount);
    }
    
    @end
    

    main

    #import <Foundation/Foundation.h>
    #import "Blog.h"
    #import "User.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            User *newUser = [[User alloc] init];
            [newUser setValue:@"张三" forKey:@"name"];
            [newUser setValue:@"18" forKey:@"age"];
            [newUser showUserInfo];
            
            Blog *newBlog = [[Blog alloc] init];
            [newUser setValue:newBlog forKey:@"blog"];
            [newUser setValue:@"100" forKeyPath:@"blog.essayCount"];
            [newUser showUserInfo];
        }
        return 0;
    }
    

    测试结果:

    2016-01-10 23:29:17.809 KVC_KVO[45598:582500] userName=张三,userAge=18,essayCount=0
    2016-01-10 23:29:17.810 KVC_KVO[45598:582500] userName=张三,userAge=18,essayCount=100
    Program ended with exit code: 0

    通过上面的例子,KVC使用起来比较简单,但它如何对一个属性进行读取和设置的呢?(假设现在要利用KVC对name进行读取)

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

    KVO(键值监听)

    KVO是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO。

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

    1. 注册指定Key路径的监听器: addObserver: forKeyPath: options: context:
    2. 删除指定Key路径的监听器: removeObserver: forKeyPath、removeObserver: forKeyPath: context:
    3. 回调监听: observeValueForKeyPath: ofObject: change: context:

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

    1. 通过addObserver: forKeyPath: options: context:为被监听对象(它通常是数据模型)注册监听器
    2. 重写监听器的observeValueForKeyPath: ofObject: change: context:方法

    我们对User.m进行升级

    #import "User.h"
    
    @implementation User
    
    - (void)showUserInfo {
        NSLog(@"userName=%@,userAge=%d,essayCount=%d",_name,_age,_blog.essayCount);
    }
    
    //重写setBlog
    -(void)setBlog:(Blog *)blog{
        _blog=blog;
        //添加对blog的监听
        [self.blog addObserver:self forKeyPath:@"essayCount" options:NSKeyValueObservingOptionNew context:nil];
    }
    
    //重写observeValueForKeyPath方法,当blog 文章数变化后此处获得通知
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
        if([keyPath isEqualToString:@"essayCount"]){//这里只处理balance属性
            NSLog(@"keyPath=%@,object=%@,newValue=%.2f,context=%@",keyPath,object,[[change objectForKey:@"new"] floatValue],context);
        }
    }
    
    -(void)dealloc{
        NSLog(@"我被销毁了....");
        [self.blog removeObserver:self forKeyPath:@"essayCount"];//移除监听
    }
    
    @end
    

      

    更新main.m

    #import <Foundation/Foundation.h>
    #import "Blog.h"
    #import "User.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            User *newUser = [[User alloc] init];
            newUser.name = @"张三";
            newUser.age = 19;
            [newUser showUserInfo];
            
            Blog *newBlog = [[Blog alloc] init];
            newUser.blog = newBlog;
            newBlog.essayCount = 500;
            [newUser showUserInfo];
            newBlog.essayCount = 2000;
            [newUser showUserInfo];
        }
        return 0;
    }
    

    新测试结果

    2016-01-11 00:31:51.221 KVC_KVO[46761:703550] userName=张三,userAge=19,essayCount=0
    2016-01-11 00:31:51.223 KVC_KVO[46761:703550] keyPath=essayCount,object=<Blog: 0x100300140>,newValue=500.00,context=(null)
    2016-01-11 00:31:51.223 KVC_KVO[46761:703550] userName=张三,userAge=19,essayCount=500
    2016-01-11 00:31:51.224 KVC_KVO[46761:703550] keyPath=essayCount,object=<Blog: 0x100300140>,newValue=2000.00,context=(null)
    2016-01-11 00:31:51.224 KVC_KVO[46761:703550] userName=张三,userAge=19,essayCount=2000
    2016-01-11 00:31:51.224 KVC_KVO[46761:703550] 我被销毁了....

    哈哈... 至此这就是一个典型的KVO应用了。

     

  • 相关阅读:
    UVA 11925 Generating Permutations 生成排列 (序列)
    UVA 1611 Crane 起重机 (子问题)
    UVA 11572 Unique snowflakes (滑窗)
    UVA 177 PaperFolding 折纸痕 (分形,递归)
    UVA 11491 Erasing and Winning 奖品的价值 (贪心)
    UVA1610 PartyGame 聚会游戏(细节题)
    UVA 1149 Bin Packing 装箱(贪心)
    topcpder SRM 664 div2 A,B,C BearCheats , BearPlays equalPiles , BearSorts (映射)
    UVA 1442 Cave 洞穴 (贪心+扫描)
    UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)
  • 原文地址:https://www.cnblogs.com/superdo/p/5119840.html
Copyright © 2011-2022 走看看