//Dictionary赋值
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:@"" forKey:@""];
[dic setObject:@"" forKey:@""];
字典里上面两句的区别(也仅限于字典)就是:
1, setObject:forkey:中value是不能够为nil的,不然会报错。
setValue: forKey:中value能够为nil,但是当value为nil的时候,会自动调用removeObject:forKey方法
2, setValue:forKey:中key的参数只能够是NSString类型,而setObject:forKey:的可以是任何类型
- (void)setValue:(id)value forKey:(NSString *)key;
- (void)removeObjectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
[NSNumber numberWithInt:10]等价于@(10)也就是@10
KVC的全称是Key-Value Coding,俗称"键值编码",可以通过一个key来访问某个属性
常见的API有
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
// 百思里面更换tabBar
[self setValue:[[KYTabBar alloc] init] forKeyPath:@"tabBar"];
首先一个.h里声明一个age只读属性的话@property (nonatomic , assign , readonly) int age;,.m如果不声明age为读写,那么是没法通过self.age = 1来赋值的,会报错提示该属性只读。但是.m里可以用_age = 1来赋值。那么会问,为什么外界不能通过_age来赋值呢,是因为_age私有在类里了。
//有个MJPerson类
#import <Foundation/Foundation.h>
@interface MJCat : NSObject
@property (assign, nonatomic) int weight;
@end
@interface MJPerson : NSObject
@property (assign, nonatomic) int age;
@property (assign, nonatomic) MJCat *cat;
@end
//在ViewController里实例化MJPerson
MJPerson *person = [[MJPerson alloc] init];
person.age = 10; //给age赋值
[person setValue:@20 forKey:@"age"]; //给age赋值
[person setValue:@30 forKeyPath:@"age"]; //给age赋值
NSLog(@"%@", [person valueForKey:@"age"]); //给age取值
NSLog(@"%@", [person valueForKeyPath:@"age"]);//给age取值
既然setValue: forKey和setValue: forKeyPath都可以给person赋值,那么setValue: forKeyPath有什么用呢,可以理解为forKeyPath是forKey的高级版,额外提供了根据“路径”赋值。
比如[person setValue:@10 forKeyPath:@"cat.weight"];//MJPerson里有个MJCat命名cat,MJCat里有个weight
论证 通过KVC修改属性会触发KVO吗?
会
写个MJPerson类
@interface MJPerson : NSObject
@property (nonatomic , assign) int age;
@end
控制器里
- (void)viewDidLoad {
[super viewDidLoad];
MJPerson *p1 = [[MJPerson alloc]init];
p1.age = 3;
[p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
[p1 setValue:@10 forKey:@"age"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
NSLog(@"---%@", change);
}
可以看到打印
2019-11-21 16:18:50.406089+0800 Test[7019:234396] ---{
kind = 1;
new = 10;
old = 3;
}
先来验证[person setValue:@10 forKey:@"age"]原理
//首先新建一个person类 ,MJPerson.h里什么变量属性都屏蔽的那种
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject
{
@public
// int age;
// int isAge;
// int _isAge;
// int _age;
}
//@property (assign, nonatomic) int age;
@end
//person类 ,MJPerson.m里先声明一些方法,暂时屏蔽,一步步证明
#import "MJPerson.h"
@implementation MJPerson
//- (int)getAge
//{
// return 11;
//}
//- (int)age
//{
// return 12;
//}
//- (int)isAge
//{
// return 13;
//}
//- (int)_age
//{
// return 14;
//}
//- (void)setAge:(int)age
//{
// NSLog(@"setAge: - %d", age);
//}
//- (void)_setAge:(int)age
//{
// NSLog(@"_setAge: - %d", age);
//}
//- (void)willChangeValueForKey:(NSString *)key
//{
// [super willChangeValueForKey:key];
// NSLog(@"willChangeValueForKey - %@", key);
//}
//
//- (void)didChangeValueForKey:(NSString *)key
//{
// NSLog(@"didChangeValueForKey - begin - %@", key);
// [super didChangeValueForKey:key];
// NSLog(@"didChangeValueForKey - end - %@", key);
//}
// 默认的返回值就是YES
//+ (BOOL)accessInstanceVariablesDirectly
//{
// return YES;
//}
@end
第一步
[p1 setValue:@10 forKey:@"age"];首先会按照
setKey:
_setKey:
顺序查找方法,找到后传递参数进行赋值,调用方法,没找到执行第二步
那么,如何论证第一步呢
先把这句放开
@property (assign, nonatomic) int age;
再把这句放开
- (void)setAge:(int)age
{
NSLog(@"setAge: - %d", age);
}
打断点就能验证是通过setKey修改
然后上句setAge屏蔽了,然后属性也屏蔽了
在person类声明成员变量{ //为什么要成员变量呢,因为属性会默认生成set方法,没法验证_setAge
@public
int age;
}
再把这句放开,即可论证第一步了
- (void)_setAge:(int)age
{
NSLog(@"_setAge: - %d", age);
}
所以,[p1 setValue:@10 forKey:@"age"]首先会找setKey方法,找不到会找_setKey方法,再找不到会执行第二步
第二步
查看accessInstanceVariablesDirectly方法的返回值
// 默认的返回值就是YES
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
方法的返回值,如果是yes,执行第三步,如果是No,直接调用setValue:forUndefinedKey:并抛出异常NSUnknownKeyException
第三步
按照
_key
_isKey
key
isKey
顺序查找成员变量,找到直接赋值
//@interface MJPerson : NSObject
//{
//@public
// int _age;
// int _isAge;
// int age;
// int isAge;
//}
//
//@end
找不到直接调用setValue:forUndefinedKey:并抛出异常NSUnknownKeyException
可能会有个疑问,成员变量不是没有set吗,直接修改成员变量不会触发kvo,为啥kvo修改了成员变量后依旧会触发kvo呢?因为kvc里手动触发kvo,也就是willchangge和didchange。
在MJPerson类里写好下面两个方法,可以看到下面俩方法会被调用,所以就是kvo修改了成员变量后依旧会触发kvo,因为手动触发了。
- (void)willChangeValueForKey:(NSString *)key
{
NSLog(@"willChangeValueForKey: - begin");
[super willChangeValueForKey:key];
NSLog(@"willChangeValueForKey: - end");
}
- (void)didChangeValueForKey:(NSString *)key
{
NSLog(@"didChangeValueForKey: - begin");
[super didChangeValueForKey:key];
NSLog(@"didChangeValueForKey: - end");
}
验证[person valueForKey:@"age"]原理
第一步
按照
getKey
key
isKey
_key
顺序查找方法
也就是
- (int)getAge
{
return 11;
}
- (int)age
{
return 12;
}
- (int)isAge
{
return 13;
}
- (int)_age
{
return 14;
}
能找到就返回值,找不到执行第二步
第二步
查看
// 默认的返回值就是YES
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
方法的返回值,yes就执行第三步,No就调用valueForUndefinedKey:
并抛出异常NSUnknownKeyException
第三步
按照
_key
_isKey
key
isKey
顺序查找成员变量,找到直接取值,找不到调用valueForUndefinedKey:
并抛出异常NSUnknownKeyException
论证[p1 setValue:@10 forKey:@"age"]和[person valueForKey:@"age"]的过程也就是KVC赋值和取值的过程
KVC的键值编码技术从可以强大的更改一个类私有成员的角度来说,是会破坏面向对象编程思想
网友评论