“`”
<h2>1 KVC(KeyValueCoding)</h2>
<h3>1.1 KVC 常用的方法</h3>
<pre><code class=""language-objc"" lang=""objc"">(1)赋值类方法
– (void)setValue:(nullable id)value forKey:(NSString *)key;
– (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
– (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
– (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
(2)取值类方法
// 能取得私有成员变量的值
– (id)valueForKey:(NSString *)key;
– (id)valueForKeyPath:(NSString *)keyPath;
– (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;
</code></pre>
<h3>1.2 KVC 底层实现原理</h3>
<pre><code class=""language-objc"" lang=""objc"">当一个对象调用setValue:forKey: 方法时,方法内部会做以下操作:
1.判断有没有指定key的set方法,如果有set方法,就会调用set方法,给该属性赋值
2.如果没有set方法,判断有没有跟key值相同且带有下划线的成员属性(_key).如果有,直接给该成员属性进行赋值
3.如果没有成员属性_key,判断有没有跟key相同名称的属性.如果有,直接给该属性进行赋值
4.如果都没有,就会调用 valueforUndefinedKey 和setValue:forUndefinedKey:方法
</code></pre>
<h3>1.3 KVC 的使用场景</h3>
<h4>1.3.1 赋值</h4>
(1) KVC 简单属性赋值
<pre><code class=""language-objc"" lang=""objc"">Person *p = [[Person alloc] init];
// p.name = @""jack"";
// p.money = 22.2;
使用setValue: forKey:方法能够给属性赋值,等价于直接给属性赋值
[p setValue:@""rose"" forKey:@""name""];
[p setValue:@""22.2"" forKey:@""money""];
</code></pre>
(2) KVC复杂属性赋值
<pre><code class=""language-objc"" lang=""objc"">//给Person添加 Dog属性
Person *p = [[Person alloc] init];
p.dog = [[Dog alloc] init];
// p.dog.name = @""阿黄"";
1)setValue: forKeyPath: 方法的使用
//修改p.dog 的name 属性
[p.dog setValue:@""wangcai"" forKeyPath:@""name""];
[p setValue:@""阿花"" forKeyPath:@""dog.name""];
2)setValue: forKey: 错误用法
[p setValue:@""阿花"" forKey:@""dog.name""];
NSLog(@""%@"", p.dog.name);
3)直接修改私有成员变量
[p setValue:@""旺财"" forKeyPath:@""_name""];
</code></pre>
(3) 添加私有成员变量
<pre><code class=""language-objc"" lang=""objc"">Person 类中添加私有成员变量_age
[p setValue:@""22"" forKeyPath:@""_age""];
</code></pre>
<h4>1.3.2 字典转模型</h4>
<pre><code class=""language-objc"" lang=""objc"">(1)简单的字典转模型
+(instancetype)videoWithDict:(NSDictionary *)dict
{
JLVideo *videItem = [[JLVideo alloc] init];
//以前
// videItem.name = dict[@""name""];
// videItem.money = [dict[@""money""] doubleValue] ;
//KVC,使用setValuesForKeysWithDictionary:方法,该方法默认根据字典中每个键值对,调用setValue:forKey方法
// 缺点:字典中的键值对必须与模型中的键值对完全对应,否则程序会崩溃
[videItem setValuesForKeysWithDictionary:dict];
return videItem;
}
(2)复杂的字典转模型
注意:复杂字典转模型不能直接通过KVC 赋值,KVC只能在简单字典中使用,比如:
NSDictionary *dict = @{
@""name"" : @""jack"",
@""money"": @""22.2"",
@""dog"" : @{
@""name"" : @""wangcai"",
@""money"": @""11.1"",
}
};
JLPerson *p = [[JLPerson alloc]init]; // p是一个模型对象
[p setValuesForKeysWithDictionary:dict];
内部转换原理:
// [p setValue:@""jack"" forKey:@""name""];
// [p setValue:@""22.2"" forKey:@""money""];
// [p setValue:@{
// @""name"" : @""wangcai"",
// @""money"": @""11.1"",
//
// } forKey:@""dog""]; //给 dog赋值一个字典肯定是不对的
(3)KVC解析复杂字典的正确步骤
NSDictionary *dict = @{
@""name"" : @""jack"",
@""money"": @""22.2"",
@""dog"" : @{
@""name"" : @""wangcai"",
@""price"": @""11.1"",
},
//人有好多书
@""books"" : @[
@{
@""name"" : @""5分钟突破iOS开发"",
@""price"" : @""19.8""
},
@{
@""name"" : @""3分钟突破iOS开发"",
@""price"" : @""24.8""
},
@{
@""name"" : @""1分钟突破iOS开发"",
@""price"" : @""29.8""
}
]
};
XMGPerson *p = [[XMGPerson alloc] init];
p.dog = [[XMGDog alloc] init];
[p.dog setValuesForKeysWithDictionary:dict[@""dog""]];
//保存模型的可变数组
NSMutableArray *arrayM = [NSMutableArray array];
for (NSDictionary *dict in dict[@""books""]) {
//创建模型
Book *book = [[Book alloc] init];
//KVC
[book setValuesForKeysWithDictionary:dict];
//将模型保存
[arrayM addObject:book];
}
p.books = arrayM;
备注:
(1)当字典中的键值对很复杂,不适合用KVC;
(2)服务器返还的数据,你可能不会全用上,如果在模型一个一个写属性非常麻烦,所以不建议使用KVC字典转模型
</code></pre>
<h4>1.3.3 取值</h4>
(1) 模型转字典
<pre><code class=""language-objc"" lang=""objc""> Person *p = [[Person alloc]init];
p.name = @""jack"";
p.money = 11.1;
//KVC取值
NSLog(@""%@ %@"", [p valueForKey:@""name""], [p valueForKey:@""money""]);
//模型转字典, 根据数组中的键获取到值,然后放到字典中
NSDictionary *dict = [p dictionaryWithValuesForKeys:@[@""name"", @""money""]];
NSLog(@""%@"", dict);
</code></pre>
(2) 访问数组中元素的属性值
<pre><code class=""language-objc"" lang=""objc"">Book *book1 = [[Book alloc] init];
book1.name = @""5分钟突破iOS开发"";
book1.price = 10.7;
Book *book2 = [[Book alloc] init];
book2.name = @""4分钟突破iOS开发"";
book2.price = 109.7;
Book *book3 = [[Book alloc] init];
book3.name = @""1分钟突破iOS开发"";
book3.price = 1580.7;
// 如果valueForKeyPath:方法的调用者是数组,那么就是去访问数组元素的属性值
// 取得books数组中所有Book对象的name属性值,放在一个新的数组中返回
NSArray *books = @[book1, book2, book3];
NSArray *names = [books valueForKeyPath:@""name""];
NSLog(@""%@"", names);
//访问属性数组中元素的属性值
Person *p = [[Person alloc]init];
p.books = @[book1, book2, book3];
NSArray *names = [p valueForKeyPath:@""books.name""];
NSLog(@""%@"", names);
</code></pre>
<h2>2 KVO (Key Value Observing)</h2>
<h3>2.1 KVO 的底层实现原理</h3>
<pre><code class=""language-objc"" lang=""objc"">(1)KVO 是基于 runtime 机制实现的
(2)当一个对象(假设是person对象,对应的类为 JLperson)的属性值age发生改变时,系统会自动生成一个继承自JLperson的类NSKVONotifying_JLPerson,在这个类的 setAge 方法里面调用
[super setAge:age];
[self willChangeValueForKey:@""age""];
[self didChangeValueForKey:@""age""];
三个方法,而后面两个方法内部会主动调用
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context方法,在该方法中可以拿到属性改变前后的值.
</code></pre>
<h3>2.2 KVO的作用</h3>
<ul>
<li>作用:能够监听某个对象属性值的改变</li>
</ul>
<pre><code class=""language-objc"" lang=""objc"">// 利用KVO监听p对象name 属性值的改变
Person *p = [[XMGPerson alloc] init];
p.name = @""jack"";
/* 对象p添加一个观察者(监听器)
Observer:观察者(监听器)
KeyPath:属性名(需要监听哪个属性)
*/
[p addObserver:self forKeyPath:@""name"" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@""123""];
/**
* 利用KVO 监听到对象属性值改变后,就会调用这个方法
*
* @param keyPath 哪一个属性被改了
* @param object 哪一个对象的属性被改了
* @param change 改成什么样了
*/
– (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
// NSKeyValueChangeNewKey == @""new""
NSString *new = change[NSKeyValueChangeNewKey];
// NSKeyValueChangeOldKey == @""old""
NSString *old = change[NSKeyValueChangeOldKey];
NSLog(@""%@-%@"",new,old);
}
</code></pre>
<pre><code> "“`
Was this helpful?
0 /
0