KVC的基本用法


前言

提示:这里可以添加本文要记录的大概内容:


简单介绍

KVC(KeyValueCoding)俗称“键值编码,是指可以通过一个key来访问某个属性
在iOS中,提供了一种方法通过使用属性的名称(key)来间接访问属性对象的方法,这个方法可以不通过setter/getter方法来访问对象的属性
KVC提供了一种间接访问其属性或成员变量的机制,可以通过字符串来访问其对应的属性方法或者成员变量
KVC还可以访问私有变量。某些情况下,KVC还可以帮助简化代码

访问对象属性

常用API


//通过key来取值
- (id)valueForKey:(NSString *)key;
//通过keyPath来取值
- (id)valueForKeyPath:(NSString *)keyPath;
//通过key来赋值
- (void)setValue:(id)value forKey:(NSString *)key;
//通过keyPath来赋值
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

key和keyPath

key:只能接收当前类所具有的属性,不管是自己的还是从父类继承的
keyPath:除了能接受当前类的属性,还能接受当前类属性的属性。(可以接收关系链)

- (void)setValue:(id)value forKey:(NSString *)key;

//  Dog.h
//  KVC
//
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@class Bone;
@interface Dog : NSObject{
    @package
    NSString* gender;
    NSString* _gender;
}
@property(nonatomic, copy)NSString* name;
@property(nonatomic, strong)Bone* bone;
@property(nonatomic, assign)int weight;
@end

NS_ASSUME_NONNULL_END




//
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Bone : NSObject
@property (nonatomic, strong)NSString* type;
@property (nonatomic, assign)NSInteger* weight;
@end

NS_ASSUME_NONNULL_END




//  main.m
//  KVC
//
//

#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Bone.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Dog* dog = [[Dog alloc]init];
        dog.bone = [[Bone alloc] init];
        [dog setValue:@"huacheng" forKey:@"name"];
        [dog.bone setValue:@"lanzhan" forKey:@"type"];
        NSLog(@"%@",[dog valueForKey:@"name"]);
        NSLog(@"%@",[dog.bone valueForKey:@"type"]);
        
    }
    return 0;
}

在这里插入图片描述
调用setValue: forKey:

  • 优先调用set:方法。代码通过setter方法完成设置。
  • 如果没有找到setName:方法,检查 + (BOOL)accessInstanceVariablesDirectly(默认返回YES)方法是否返回YES,如果设置返回为NO,执行- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key方法。
  • 如果该类没有setKey:方法,也没有_成员变量,KVC会搜索_is< Key >的成员变量;
  • 如果该类没有set:方法,也没有_和_is< key>的成员变量,KVC 机制会呢继续搜索和is< key>的成员变量。在进行赋值操作。
  • 如果上面所述都不存在,执行- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;方法,默认抛出异常。

//  Dog.h
//  KVC
//
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//@class Bone;
@interface Dog : NSObject{
    NSString* toSetName;
    NSString* isName;
    NSString* _name;
    NSString* _isName;
}
@property(nonatomic, copy)NSString* name;
//@property(nonatomic, strong)Bone* bone;
@property(nonatomic, assign)int weight;



@end

NS_ASSUME_NONNULL_END


//  Dog.m
//  KVC
//
//

#import "Dog.h"

@implementation Dog
- (void)setName:(NSString *)name{
    toSetName = name;
}
- (NSString*)getName{
    return toSetName;
}

+ (BOOL)accessInstanceVariablesDirectly{
    return NO;
}
- (id)valueForUndefinedKey:(NSString *)key{
    NSLog(@"error,key%@不存在",key);
    return 0;
    
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"error,key%@不存在",key);
}
@end

//  main.m
//  KVC
//
//

#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Bone.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Dog* dog = [[Dog alloc] init];
        [dog setValue:@"huacheng" forKey:@"name"];
        NSLog(@"%@",[dog valueForKey:@"toSetName"]);
//        Dog* dog = [[Dog alloc]init];
//        dog.bone = [[Bone alloc] init];
//        [dog setValue:@"huacheng" forKey:@"name"];
//        [dog.bone setValue:@"lanzhan" forKey:@"type"];
//        NSLog(@"%@",[dog valueForKey:@"name"]);
//        NSLog(@"%@",[dog.bone valueForKey:@"type"]);
        
    }
    return 0;
}

在这里插入图片描述
说明我们在重写方法+ (BOOL)accessInstanceVariablesDirectly;后,没有找到setName方法,不在去找name成员变量,而是直接调用- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;方法。修改函数返回值为YES后,可以得到我们想要的东西

在这里插入图片描述

- (id)valueForKey:(NSString *)key;

调用valueForKey:

  • 按照get< Key>,,is< key>的顺序方法查找getter方法。如果是BOOL或Int类型,会将其包装成NSNmuber对象。
  • 如果getter方法没有找到,KVC则会查找countOf< Key>,objectIn< Key>AtIndex或< Key>AtIndexes等方法。如果countOf< Key>方法和另外两个方法中的一个方法被找到,返回一个可以相应NSArray所有方法的集合代理(NSKeyValueArray,是NSArray的子类)。调用这个集合的代理方法,或者说给这几个代理集合发送属于NSArray的方法。就会以countOf< Key>,objectIn< Key>AtIndex或< Key>AtIndexes这几个方法的组合形式调用。还有一个可选的getKey:range:方法。
  • 如果上面的方法没有找到,那么会同时查找countOf< Key>,enumeratorOf< Key>,memberOf< Key>格式的方法。如果这三个方法都找到,返回一个可以响应NSSet的方法的代理集合。给这个集合发送NSSet的消息,就会以countOf< Key>,enumeratorOf< Key>,memberOf< Key>组合的形式调用。
  • 如果还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly;如果返回YES,按照_< key>,is< Key>,key,isKey的顺序搜索成员变量名。如果方法+ (BOOL)accessInstanceVariablesDirectly;返回NO,调用方法- (nullable id)valueForUndefinedKey:(NSString *)key;
  • 如果还没有找到,调用方法- (nullable id)valueForUndefinedKey:(NSString *)key
//  Array.h
//  KVC
//
//  Created by 王璐 on 2022/9/21.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Array : NSObject
@property (nonatomic,readwrite,assign) NSUInteger count;
@property (nonatomic,copy) NSString *arrName;
- (void)incrementCount;
- (NSUInteger)countOfNumbers;
- (id)objectInNumbersAtIndex:(NSUInteger)index;
@end

NS_ASSUME_NONNULL_END



//  Array.m
//  KVC
//
//

#import "Array.h"

@implementation Array
- (void)incrementCount {
    self.count++;
}
- (NSUInteger)countOfNumbers {
    return self.count;
}

- (id)objectInNumbersAtIndex:(NSUInteger)index {     //当key使用numbers时,KVC会找到这两个方法。
    return @(index * 2);
}
- (NSInteger)getNum {                 
    return 10;
}
- (NSInteger)num {                       
    return 11;
}
- (NSInteger)isNum {                   
    return 12;
}
@end



//  main.m
//  KVC
//
//

#import <Foundation/Foundation.h>
#import "Dog.h"
#import "Bone.h"
#import "Array.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Array* arr = [Array new];
        NSNumber* num =   [arr valueForKey:@"num"];
        NSLog(@"%@",num);
        id ar = [arr valueForKey:@"numbers"];
        NSLog(@"%@",NSStringFromClass([ar class]));
        NSLog(@"0:%@     1:%@     2:%@     3:%@",ar[0], ar[1], ar[2], ar[3]);
        [arr incrementCount];
        NSLog(@"%lu",(unsigned long)[ar count]);
        [arr incrementCount];  
        NSLog(@"%lu",(unsigned long)[ar count]);
        [arr setValue:@"newName" forKey:@"arrName"];
        NSLog(@"%@",[arr valueForKey:@"arrName"]);
    }
    return 0;
}

结果:
在这里插入图片描述

其他方法举例

NSKeyValueCoding中的其他方法


+ (BOOL)accessInstanceVariablesDirectly;
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索

- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。

- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。

- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。

- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山河丘壑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值