KVC原理及应用场景

KVC使用

基本使用

声明的类:


// .h文件
@interface ModelStudent : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger sex;

@end

// .m文件
#import "ModelStudent.h"

@interface ModelStudent ()

@property NSString *city;

@end

@implementation ModelStudent 

@end

在ViewController里:

    self.student1 = [[ModelStudent alloc] init];
    // name是公有属性
    [_student1 setValue:@"yyy" forKey:@"name"];
    NSLog(@"%@", [_student1 valueForKey:@"name"]);
    // city是私有属性
    [_student1 setValue:@"xian" forKey:@"city"];
    NSLog(@"%@", [_student1 valueForKey:@"city"]);

在这里插入图片描述
公有私有都可以访问和设置

多重属性赋值

student里加一个dog属性

@interface ModelDog : NSObject

@property (nonatomic, strong) NSString *name;

@end


@interface ModelStudent : NSObject<NSCoding, NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger sex;
@property (nonatomic, strong) ModelDog *dog;

@end

ViewController里:

    ModelStudent *student = [[ModelStudent alloc] init];
    student.dog = [[ModelDog alloc] init];
    [student.dog setValue:@"aa" forKey:@"name"];
    NSLog(@"%@", [student.dog valueForKey:@"name"]);
    [student setValue:@"ww" forKeyPath:@"dog.name"];
    NSLog(@"%@", [student valueForKeyPath:@"dog.name"]);

在这里插入图片描述

字典转模型

    NSDictionary *dic = @{@"name":@"book", @"age" : @"66"};
    StudentModel *model = [[StudentModel alloc] init];
    
    [model setValuesForKeysWithDictionary:dic];
    
    NSLog(@"model.name : %@",model.name);
    NSLog(@"model.num : %@",model.age);

在这里插入图片描述
但是,如果 model 属性和 dic 不匹配:
第一种情况,model多一个属性:这样程序没问题,model多出的属性会是nil
第二种情况,model少一个属性:程序会崩溃
第三种情况,model的属性和dic不对应,程序会崩溃
也就是只要dic的属性model中没有,就会崩溃,解决办法是重写方法 -(void)setValue:(id)value forUndefinedKey:(NSString *)key

@interface StudentModel : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *age;
@property (nonatomic, strong) NSString *studentSex;

@end

@implementation StudentModel

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if([key isEqualToString:@"sex"]){
        self.studentSex = (NSString *)value;
    }
}

@end

ViewController里:

    NSDictionary *dic = @{@"name":@"book", @"age" : @"66", @"sex": @"male"};
    StudentModel *model = [[StudentModel alloc] init];
    
    [model setValuesForKeysWithDictionary:dic];
    
    NSLog(@"model.name : %@", model.name);
    NSLog(@"model.num : %@", model.age);
    NSLog(@"model.sex : %@", model.studentSex);

模型转字典

    NSDictionary *dic = @{@"name" : @"book", @"age" : @"66", @"sex" : @"male"};
    StudentModel *model = [[StudentModel alloc] init];
    
    [model setValuesForKeysWithDictionary:dic];
    
    NSDictionary *modelDic = [model dictionaryWithValuesForKeys:@[@"name", @"age", @"studentSex"]];
    NSLog(@"modelDic : %@", modelDic);

在这里插入图片描述

KVC 原理

- (void)viewDidLoad {
    ModelStudent *student = [[ModelStudent alloc] init];
    // 添加KVO监听
    [student addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
    
    // 通过KVC修改name属性
    [student setValue:@"qqq" forKey:@"name"];
    
    // 移除KVO监听
    [student removeObserver:self forKeyPath:@"name"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"触发了KVO   -   %@", change);
}

在这里插入图片描述
我们从KVO原理中可以知道,只要实现willChangeValueForKey:didChangeValueForKey:方法就可以调用KVO方法,即在KVC中调用了这两个方法
验证一下:

// ModelStudent.m
- (void)setName:(NSString *)name {
    _name = name;
    NSLog(@"setName: - %@", name);
}

- (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);
}

在这里插入图片描述
说明是在didChangeValueForKey中调用了setName:方法,触发了KVO
打印类看一下:

    NSLog(@"%s", object_getClassName(student));

在这里插入图片描述

调用setValue:forKey:方法顺序

当调用setValue:forKey:时 ,程序会先通过setter方法(也就是setKey:方法),对属性进行设置;
如果没有找到setKey:方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,该方法默认返回YES;如果重写方法成了NO,则调用- (nullable id)setValueForUndefinedKey:,抛出异常,程序崩溃。
返回YES就去找成员变量并直接赋值。

// ModelStudent.h
@interface ModelStudent : NSObject {
    @public
    NSString *name;
    NSString *isName;
    NSString *_isName;
    NSString *_name;
}
@end

// ViewController.m
    ModelStudent *student = [[ModelStudent alloc] init];
   
    [student setValue:@"qqq" forKey:@"name"];
    NSLog(@"%@", [student valueForKey:@"name"]);

在这里插入图片描述
可以看到是先给_name赋值了
我们把_name注释了看看:

@interface ModelStudent : NSObject<NSCoding, NSCopying> {
    @public
    NSString *name;
    NSString *isName;
    NSString *_isName;
//    NSString *_name;
}

在这里插入图片描述
下一个是_isName,剩下的按顺序是:name, isName, 就不在这里一一试了,大家可以自己试试

调用Value:forKey:方法顺序

按先后顺序搜索getKeykeyisKey_getKey_key五个方法,若某一个方法被实现,取到的即是方法返回的值,后面的方法不再运行。如果是BOOL或者Int等值类型, 会将其包装成一个NSNumber对象。

// ModelStudent.m
- (void)getName {
    NSLog(@"getName");
}

- (void)name {
    NSLog(@"name");
}

- (void)isName {
    NSLog(@"isName");
}

- (void)_getName {
    NSLog(@"_getName");
}

- (void)_name {
    NSLog(@"_name");
}

在这里插入图片描述
然后注释getName方法,看调用哪个方法,这里就不一一验证了
如果五个方法都没有,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,该方法默认返回YES;如果重写方法成了NO,则调用- (nullable id)valueForUndefinedKey:,抛出异常,程序崩溃。
返回YES就去找成员变量并直接取值。
取值顺序为:_key, _isKey, key, isKey,这里也就不验证了

参考文献

KVC使用场景总结
KVC原理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值