zoukankan      html  css  js  c++  java
  • iOS的设计模式-KVC

          关于iOS的设计模式,相信大家肯定不会陌生了,其中最常见的是MVC,也就是模型-视图-控制器模式,也是我们经常用的,这个这里就不在叙述了,这里主要讲一讲KVC这种模式,若文中有纰漏,请广大博友出来指正~~

    聊聊设计模式

         虽然设计模式时时刻刻存在我们的项目工程里,但是我们却很少去注意它们,只是不自觉的去运用。

         在软件设计领域,设计模式是对通用问题的可复用的解决方案。设计模式是一系列帮你写出更可理解和复用代码的模板,设计模式帮你创建松耦合的代码以便你不需要费多大力就可以改变或者替换代码中的组件。

         所以多知道一些设计模式有助于我们的代码更加规范,清晰。使我们的代码风格更加好。

    深扒一下KVC     

         KVC 全称 key valued coding 键值编码

         反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性.JAVA,C#都有这个机制。ObjC也有,所以你根部不必进行任何操作就可以进行属性的动态读写,就是KVC。

         KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作。

    Person.h

    @interface Person : NSObject
    {
        int weight;
    }
    @property(nonatomic,readonly,copy) NSString *name;
    @property(nonatomic,readonly, assign) int age;
    @property(nonatomic,strong) Dog * dog;
    @property(nonatomic,assign) id ID;
    
    -(instancetype)initWithDict:(NSDictionary *)dict;
    
    @end

    Person.m

    @implementation Person
    {
        int _height;
    }
    
    -(instancetype)initWithDict:(NSDictionary *)dict
    {
        if (self=[super init])
        {
            //字典转模型的常用语句
            [self setValuesForKeysWithDictionary:dict];
        }
        return self;
    }
    
    //当key的值是没有定义时,设置会执行的方法
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
        
        if ([key isEqualToString:@"id"])
        {
            self.ID = value;
        }
    }
    
    
    //当key的值是没有定义时,取值时执行的方法
    - (id)valueForUndefinedKey:(NSString *)key {
            
        if ([key isEqualToString:@"id"]) {
            return self.ID;
        }
        return [NSNull null];
    }

    main.m

    Person * p1 = [[Person alloc]init];
    Dog * d1 = [[Dog alloc] init];
    p1.dog = d1;
       
    //setValue:属性值 forKey:属性名(用于简单路径)  
    //使用KVC间接修改对象属性时,系统会自动判断对象属性的类型,并完成转换。如该程序中的“170”.      
    [p1 setValue:@"170" forKey:@"height"];
    [p1 setValue:@"70" forKey:@"weight"];
    [p1 setValue:@"1" forKey:@"id"];
    
    //setValue:属性值 forKeyPath:属性路径(用于复合路径)
    //用KVC取一个嵌套层次很深的路径的时候,只要给它一个路径就能把想要的属性给拿出来。(.可以理解为路径。一直一直进入)。能够帮助我们很方便的编码。
    [p1 setValue:@"dahuan" forKeyPath:@"dog.name"];
    
    //valueForKey:属性名 、valueForKeyPath:属性名(用于复合路径)
    NSLog(@"height = %d weight = %d id = %@ dog.name = %@" ,[[p1 valueForKey:@"height"] intValue],[[p1 valueForKey:@"weight"] intValue],[p1 valueForKey:@"id"],[p1 valueForKeyPath:@"dog.name"]);
    
    log日志:height = 170 weight = 70 id = 11111 dog.name = dahuan
    所有的属性都可以赋值成功
    
    
    NSDictionary * dict = @{@"height":@"160",
                            @"weight":@"60",
                            @"id":@"11101"
                            };
            
    Person * p2 = [[Person alloc] initWithDict:dict];
    
    NSLog(@"height = %d weight = %d id = %@",[[p2 valueForKey:@"height"] intValue],[[p2 valueForKey:@"weight"] intValue],[p2 valueForKey:@"id"]);
    
    log日志:height = 160 weight = 60 id = 11101
    所有的属性都可以赋值成功

    KVC查找属性的顺序

    1. 用@property定义的属性的key值
    2. setter方法的key值
    3. 直接访问成员变量,先找key,如果找不到,再找_key
    4. 以上三种都未找到就会调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key 方法。
    5. 如果没有重写setValue:forUndefinedKey程序会马上崩溃。

    注意:KVC可以访问成员变量,无论是否提供getter/setter方法,无论可见性是怎样,是否有readonly修饰。

    setValue:forUndefinedKey与valueForUndefinedKey的应用

    KVC的主要用途无非是ORM映射,就是将dictionary转换成model,但有些服务器返回的字段有可能是oc的关键字比如‘id’,’description’等。如上代码举得id的例子,我们无法让@property后面key值为id,于是使用大写的ID代替,KVC是区分大小写的我们不用担心。这时我们只需在setValue:forUndefinedKey把id的key值赋值给ID的key值,就可以避免关键字的尴尬。

    KVC底层实现

    比如说如下的一行KVC的代码:
     
    [person setValue:@"dahuan" forKey:@"name"];
     
    就会被编译器处理成:
     
    SEL sel = sel_get_uid ("setValue:forKey:");
    IMP method = objc_msg_lookup (person->isa,sel);
    method(person, sel, @"dahuan", @"name");

    KVC与runtime应用

    #import "MyModel.h"
    #import <objc/runtime.h>
     
    @implementation MyModel
     
    //解档
    - (id)initWithCoder:(NSCoder *)decoder
    {
        if (self = [super init]) {
            unsigned int count = 0;
            //获取类中所有成员变量名
            Ivar *ivar = class_copyIvarList([MyModel class], &count);
            for (int i = 0; i<count; i++) {
                Ivar iva = ivar[i];
                const char *name = ivar_getName(iva);
                NSString *strName = [NSString stringWithUTF8String:name];
                //进行解档取值
                id value = [decoder decodeObjectForKey:strName];
                //利用KVC对属性赋值
                [self setValue:value forKey:strName];
            }
            free(ivar);
        }
        return self;
    }
    
    //归档
    - (void)encodeWithCoder:(NSCoder *)encoder
    {
        unsigned int count;
        Ivar *ivar = class_copyIvarList([MyModel class], &count);
        for (int i=0; i<count; i++) {
            Ivar iv = ivar[i];
            const char *name = ivar_getName(iv);
            NSString *strName = [NSString stringWithUTF8String:name];
            //利用KVC取值
            id value = [self valueForKey:strName];
            [encoder encodeObject:value forKey:strName];
        }
        free(ivar);
    }
    @end

    NSArray/NSSet等都支持KVC

    Person * p1 = [[Person alloc]init];
    Dog * d1 = [[Dog alloc] init];
    d1.name = @"iPhone";
    p1.dog = d1;
            
    Person * p2 = [[Person alloc]init];
    Dog * d2 = [[Dog alloc] init];
    d2.name = @"ios";
    p2.dog = d2;
    
    NSArray *persons=@[p1,p2];
       
    NSArray *arrayM=[persons valueForKeyPath:@"dog.name"];
    NSLog(@"%@",arrayM);
    
    log日志:
    (
        iPhone,
        ios
    )

    KVC的逆向使用

    Person * p1 = [[Person alloc]init];
    [p1 setValue:@"170" forKey:@"height"];
    [p1 setValue:@"70" forKey:@"weight"];
    [p1 setValue:@"11111" forKey:@"id"];
    
            
    NSArray * arr = @[@"height",@"weight",@"id"];
    NSDictionary * dict = [p1 dictionaryWithValuesForKeys:arr];
    NSLog(@"%@",dict);
    
    log日志:
    {
        height = 170;
        id = 11111;
        weight = 70;
    }

    最后附苹果KVC官方文档:

    https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/KeyValueCoding.html

  • 相关阅读:
    scrollview嵌套listiview(解决高度问题以及两者滚动冲突问题)
    listview通过onscrollListener实现分页加载
    listview中的checkbox实现全选、反选、删除的功能
    【JavaScript】闭包应用之数据独立
    【JavaScript】闭包应用之数据缓存
    【问题:发现与解决】angularJs指令在dijit控件中的使用
    【转发】:CSS代码重构与优化之路
    写一个简易的富文本
    【html/css】若母div设置了透明度,如何才能使得里面的子div不继承母div的透明度
    【html/css】模态框的实现
  • 原文地址:https://www.cnblogs.com/LeoTai/p/5454923.html
Copyright © 2011-2022 走看看