长路漫漫,唯剑作伴--KVC、KVO、NSNotification

一、KVC

  1. 简介

    • key value coding,键值编码。通过key访问属性,不必使用明确的setter和getter方法,即可实现属性的访问,可以大大减少代码量。
  2. API

     //直接通过Key来取值
    - (nullable id)valueForKey:(NSString *)key;                         
    
     //通过Key来设值
    - (void)setValue:(nullable id)value forKey:(NSString *)key;
             
    //通过KeyPath来取值
    - (nullable id)valueForKeyPath:(NSString *)keyPath;                  
    
    //通过KeyPath来设值
    - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; 
    
     //默认返回YES,返回NO表示禁用KVC
    + (BOOL)accessInstanceVariablesDirectly;
    
    //KVC提供属性值确认的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。
    - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
    
    //这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回
    - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
    
    //如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常
    - (nullable id)valueForUndefinedKey:(NSString *)key;
    
    //和上一个方法一样,只不过是设值。
    - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
    
    //如果你在SetValue方法时面给Value传nil,则会调用这个方法
    - (void)setNilValueForKey:(NSString *)key;
    
    //输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
    - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
  3. 附:valueForKeyPath比valueForKey多了一层传递关系

    // valueForKeyPath可以获取数组中的最小值、最大值、平均值、求和。代码如下:
    + (NSString *)caculateArray:(NSArray *)array
    {
        CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
        CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
        CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
        CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
        NSLog(@"%fn%fn%fn%f",sum,avg,max,min);
        return [NSString stringWithFormat:@"%f",sum];
    }
    
    // valueForKeyPath可以获取数组中相同key的元素,获取到city数组 @[@"beijing",@"chengdu"]
    NSArray *arr = @[@{@"city":@"beijing",@"person":@{@"name":@"zhangsan"}},@{@"city":@"chengdu"}];
    [arr valueForKeyPath:@"city"] 
    
     
    // valueForKeyPath 可以使用. 来一层一层向下索引,当多个字典层级时,取子层级中的属性就非常简单了
    NSDictionary *dict1 = @{@"dic1":@{@"dic2":@{@"name":@"lisi",@"info":@{@"age":@"12"}}}};
    id res = [dict1 valueForKeyPath:@"dict1.dict2.name"];
    //res = lisi
  4. 字典转模型

    • @implementation MyApp
      -(instancetype)initWithDict:(NSDictionary *)dict{
          if (self=[super init]) {
              self.name=dict[@"name"];
              self.icon=dict[@"icon"];
          }
          return self;
      }
      +(instancetype) appWithDict:(NSDictionary *)dict{
          return [[self alloc] initWithDict:dict];
          
      }

      使用:MyApp *app=[MyApp appWithDict:dict];

    • 使用- (void)setValuesForKeysWithDictionary:(NSDictionary*)keyedValues批量赋值。为了实现对嵌套json的处理,实现-(void)setValue:(id)value forUndefinedKey:(NSString *)key方法,这样当json中键值对多余模型类中的属性时不至于直接crash。
  5. 模型转字典可以参考Runtime对字典转模型的处理,这样做更简洁和方便。

二、KVO

  •     - (void)viewDidLoad  
        {  
            [super viewDidLoad];  
          
            stockForKVO = [[StockData alloc] init];  
            [stockForKVO setValue:@"searph" forKey:@"stockName"];  
            [stockForKVO setValue:@"10.0" forKey:@"price"];      
            [stockForKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];  
          
            myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 30 )];  
            myLabel.textColor = [UIColor redColor];  
            myLabel.text = [stockForKVO valueForKey:@"price"];  
            [self.view addSubview:myLabel];  
             
            UIButton * b = [UIButton buttonWithType:UIButtonTypeRoundedRect];  
            b.frame = CGRectMake(0, 0, 100, 30);  
            [b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];  
            [self.view addSubview:b];  
          
        }  
        -(void) buttonAction  
        {  
            [stockForKVO setValue:@"20.0" forKey:@"price"];  
        }  
        -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context  
        {  
            if([keyPath isEqualToString:@"price"])  
            {  
                myLabel.text = [stockForKVO valueForKey:@"price"];  
            }  
        }  
    // 增加观察与取消观察是成对出现的,所以需要在最后的时候,移除观察者
        - (void)dealloc  
        {  
            [super dealloc];  
            [stockForKVO removeObserver:self forKeyPath:@"price"];  
            [stockForKVO release];  
        }  
  • 键值编码和键值观察是根据isa-swizzling技术来实现的,主要依据runtime的强大动态能力。
  • 总结一下,想使用KVO有三种方法:
    1)使用了KVC
    使用了KVC,如果有访问器方法,则运行时会在访问器方法中调用will/didChangeValueForKey:方法;
    没用访问器方法,运行时会在setValue:forKey方法中调用will/didChangeValueForKey:方法。
    2)有访问器方法
    运行时会重写访问器方法调用will/didChangeValueForKey:方法。
    因此,直接调用访问器方法改变属性值时,KVO也能监听到。
    3)显示调用will/didChangeValueForKey:方法。

三、NSNotification

 

posted @ 2017-03-25 02:12  来事啊  阅读(202)  评论(0编辑  收藏  举报