【iOS】——KVC


一、基本概念

KVC(Key-Value Coding)是iOS开发中一种用于间接访问对象属性的机制。通过KVC,你可以使用字符串作为键来访问对象的属性,而不是直接调用属性的访问方法。

最基本的KVC由NSkeyValueCoding协议提供支持,最基本的操作属性的两个方法如下:

setValue:属性值forkey:属性名;为指定属性设置值
valueForKey:属性名:获取指定属性的值


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface User : NSObject

@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSString * pass;

@end

NS_ASSUME_NONNULL_END



#import <Foundation/Foundation.h>
#import "User.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        User *user = [[AUser alloc] init];
        //使用KVC方式为name属性设置值
        [user setValue:@"dingzhen" forKey:@"name"];
        //使用KVC方式为pass属性设置值
        [user setValue:@"123" forKey:@"pass"];
        //使用KVC方式获取User对象的属性值
        NSLog(@"%@", [aUser valueForKey:@"name"]);
        NSLog(@"%@", [aUser valueForKey:@"pass"]);
    }
    return 0;
}



在KVC编程方式中,无论调用setValue:forKey:方法还是调用valueForKey:方法,都是通过NSString对象来指定被操作属性的,其中forKey标签用于传入属性名。

对于setValue: forKey: 方法,底层的执行机制如下:

1.程序优先考虑调用属性自动合成的setter方法
2.如果该类没有setter方法,KVC机制会搜索该类中名为传入的“_字符串”(例如_name)的成员变量无论该成员变量是在接口或者实现部分定义、无论它用哪个访问控制符修饰,这条KVC底层上是对该成员变量的赋值。
3.如果该类即没有setter方法也没有“_字符串”成员变量,那么KVC机制会搜索该类中名为字符串的成员变量。
4.如果上面三步都没有找到,那么系统会执行该对象的setValue: forUndefinedKey:方法,该方法的实现就是引发一个异常,导致程序结束。

对于valueforKey:代码, 底层的执行机制如下:

1.程序优先考虑调用属性自动合成的getter方法
2…如果该类没有getter方法,KVC机制会搜索该类中名为传入的“_字符串”(例如_name)的成员变量无论该成员变量是在接口或者实现部分定义、无论它用哪个访问控制符修饰,这条KVC底层上是对该成员变量的赋值。
3.如果该类即没有getter方法也没有“_字符串”成员变量,那么KVC机制会搜索该类中名为字符串的成员变量。
4.如果上面三步都没有找到,那么系统会执行该对象的valueforUndefinedKey:方法,该方法的实现就是引发一个异常,导致程序结束。

二、处理不存在的nil

前面提到使用KVC操作属性时,如果这些属性没有setter、getter方法,也不存在对应的成员变量时,KVC将会自动会调用setValue: forUndefinedKey:或valueForUndefinedKey:方法。系统默认该方法的实现是引发一个异常然后结束程序,如果我们有特殊的需求可以重写这两种方法。

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKApple : NSObject

@end

NS_ASSUME_NONNULL_END

#import "FKApple.h"

@implementation FKApple

- (void) setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"你尝试访问的key:【%@】并不存在!", key);
    NSLog(@"你尝试设置的value:%@", value);
}

- (void)valueForUndefinedKey:(id)key {
    NSLog(@"你访问的key:【%@】不存在",key);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKApple* apple = [[FKApple alloc] init];
        [apple setValue:@"大苹果" forKey:@"name"];
        [apple valueForKey:@"name"];
    }
    return 0;
}


三、处理nil值

当调用KVC来设置对象的属性时,如果属性的类型为基本类型并且程序传入了对应类型的值,那么程序可以正常进行,如果属性的类型为字符串类型传入nil值也能正常进行,但是基本类型传入nil值则会自动执行setNilValueForKey:方法,因此为了满足我们的需求可以重写该方法。

#import "FKItem.h"

@implementation FKItem
 - (void) setNilValueForKey:(NSString *)key {
    //如果尝试将key为price的属性设置为nil。
    if ([key isEqualToString:@"price"]) {
        //将该price设置为0
        _price = 0;
    } else {
        //回调父类的setNilValueForKey,执行默认行为
        [super setNilValueForKey:key];
    }
}
@end

四、Key路径

KVC处理操作对象的属性之外,还可以操作对象的属性的“复合属性”。
“复合属性”,KVC机制将其称为Key路径。例如,FKOrder对象中包含一个FKItem类型的item属性,而FKItem对象又包含了name属性以及price属性,因此KVC可以通过item.name和item.pricez这种路径来支持操作FKOrder对象的name,price属性。
KVC协议中为操作Key路径的方法如下:

setValue: forKeyPath: :根据Key的路径设置属性值。
valueForKeyPath: :根据Key的路径获取属性值。

示例代码如下:

#import <Foundation/Foundation.h>
#import "FKItem.h"
NS_ASSUME_NONNULL_BEGIN

@interface FKOrder : NSObject

@property (nonatomic, strong) FKItem* item;
@property (nonatomic, assign) int amount;

@end

NS_ASSUME_NONNULL_END


#import "FKOrder.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKOrder* order = [[FKOrder alloc] init];
        //使用KVC方法为amount设置属性值
        [order setValue:@"12" forKey:@"amount"];
        [order setValue:[[FKItem alloc] init] forKey:@"item"];
        //使用setValue:forKeyPath来获取复合值
        [order setValue:@"鼠标" forKeyPath:@"item.name"];
        [order setValue:[NSNumber numberWithInt:20] forKeyPath:@"item.price"];
        NSLog(@"订单包含%@个%@,单价为%@" ,[order valueForKey:@"amount"], [order valueForKeyPath:@"item.name" ],[order valueForKeyPath:@"item.price"]);
        
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值