长路漫漫,唯剑作伴--KVC、KVO、NSNotification
一、KVC
-
简介
- key value coding,键值编码。通过key访问属性,不必使用明确的setter和getter方法,即可实现属性的访问,可以大大减少代码量。
-
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;
-
附: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
-
字典转模型
-
@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。
-
- 模型转字典可以参考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