KVC和KVO

10 篇文章 0 订阅
9 篇文章 0 订阅

KVC: NSKeyValueCoding的缩写(键值编码)非正式协议(Informal Protocol)

非正式协议:不需要遵循

能做什么?

可以通过name(key)去隐式地访问某个对象的属性;

注意点:name(key)必须和属性名字一样


适用场景(什么样的类型满足KVC): 只要继承NSObject的类都是满足KVC


显示地访问某个对象的属性的方式:

方式一: setter/getter方法(accessor methods)

方式二:点语法直接访问(读/写)


怎么做?

方式一:

设置属性值

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

获取属性值

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

方式二:

设置属性值

- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

获取属性值

- (nullable id)valueForKeyPath:(NSString *)keyPath;


ViewController.m

//先用init方式初始化person对象
    TRPerson *firstPerson = [[TRPerson alloc] initWithName:@"Maggie" withAge:18];
    //点语法获取
    NSLog(@"使用点语法获取名字:%@;年龄:%d", firstPerson.name, firstPerson.age);
    //KVC方式一获取(针对都是OC对象类型)
    /* 注意点:
       1.key和属性名字一模一样;
       2.重写方法,获取一个不存在的属性的值
       3.给C语言的基本类型设置为nil,会抛异常;需要重写方法,给定初始值
     */
    NSString *nameFromKVC = [firstPerson valueForKey:@"name"];
    NSLog(@"使用KVC方式一获取名字:%@;年龄:%@", nameFromKVC, [firstPerson valueForKey:@"age"]);
    //如果使用KVC方式取一个不存在的属性值(给定不存在的key)
    NSLog(@"使用KVC方式获取不存在的属性值:%@", [firstPerson valueForKey:@"salary"]);
    
    
    //使用KVC的方式设置name/age
    TRPerson *secondPerson = [TRPerson new];
    [secondPerson setValue:@"Bob" forKey:@"name"];
    [secondPerson setValue:nil forKey:@"age"];
    NSLog(@"使用KVC方式一设置name:%@; 设置age:%@",[secondPerson valueForKey:@"name"], [secondPerson valueForKey:@"age"]);
    
    
    
    KVC设置和获取属性值的方式二(keyPath)
    TRPerson *thirdPerson = [[TRPerson alloc] initWithName:@"Jonny" withAge:20];
    TRAddress *address = [TRAddress new];
    thirdPerson.address = address;
    
    //使用KVC方式设置
    [thirdPerson setValue:@"北京市-朝阳区-中国" forKeyPath:@"address.simpleAddress"];
    [thirdPerson setValue:@"潘家园-xxx大厦-6层" forKeyPath:@"address.detailAddress"];
    //使用KVC方式取值
    NSLog(@"简单地址:%@; 详细地址:%@",[thirdPerson valueForKeyPath:@"address.simpleAddress"], [thirdPerson valueForKeyPath:@"address.detailAddress"]);

Person.h

/** 名字*/
@property (nonatomic, copy) NSString *name;
/** 年龄*/
@property (nonatomic, assign) int age;
/** 地址*/
@property (nonatomic, strong) TRAddress *address;

//初始化方式
- (instancetype)initWithName:(NSString *)name withAge:(int)age;

Person.m

- (instancetype)initWithName:(NSString *)name withAge:(int)age {
    if (self = [super init]) {
        self.name = name;
        self.age = age;
    }
    return self;
}

//重写valueForUndefinedKey方法,返回警告/文本log
//不会再报NSUnknownKeyException异常错误
- (id)valueForUndefinedKey:(NSString *)key {
    return @"当前的这个key所对应的属性不存在";
}

//如果设置某个key对应属性的值为空(设置c语言的基本的类型;翻译标量类型),自动调用下面的方法;手动设置一个初始值
- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"age"]) {
        //给定初始的年龄值
        [self setValue:@18 forKey:@"age"];
    }
}

Address.h

<span style="font-size:14px;">/** 简单地址*/
@property (nonatomic, copy) NSString *simpleAddress;
/** 详细地址*/
@property (nonatomic, copy) NSString *detailAddress;
</span>

总结:

1. KVC利用动态语言(OC/Ruby...)运行的机制

2. KVC两种设置和获取的方式(key/keyPath); 遵循原则:key/keyPath中包含值和属性名字一模一样

3.优势:

   a. 可以使用KVC方式对私有属性进行访问(设置/获取)

   b. 可以访问(设置/获取)嵌套属性的值(只能用keyPath)


KVO:(NSKeyValueObserving: 键值观察机制)

适用范围:只要是继承与NSObject所有类,适用KVO机制

做什么(机制)?

1. 监听某个/某些属性值的变化,做出响应的机制;

2. 当被观察者的值发生变化,会自动发送通知给观察者,进而观察者做出相应的反应(更新数据到界面…)


如何做? 执行四个步骤

步骤一:明确观察者和被观察者;创建两个对象

步骤二:被观察者对象需要调用addObserver方法,注册观察者

步骤三:观察者需要实现observeValueForKeyPath方法,获取观察(监听)的值(包含初始值/老值/改变后的新值)

步骤四:在观察者对象和被观察者对象释放前,需要移除观察者 (需要在适当的时候移除,否则编译器会抛异常)


KVO和NSNotification的区别?

1. 前者是观察者和被观察者对象直接联系(监听和被监听联系);后者监听对象和被监听对象和通知中心建立联系

2. 前者当监听到值变化,自动触发方法; 后者需要自己写监听的触发方法(selector方法)


ViewController.m

//1.创建观察者对象;创建被观察者对象
    TRPerson *person = [TRPerson new];
    TRBank *bank = [TRBank new];
    //2.使用KVC方式给被观察者对象设置余额
    [bank setValue:@1000.5 forKey:@"accountBalance"];
    //3.被观察者调用addObserver方法,添加观察动作
    /* 参数一:指定观察对象的属性名字(属性路径)
       参数二:给定观察的枚举值(接收到通知的时候,获取新值/老值/初始值)
       描述:注册一个观察者对象Register
     NSKeyValueObservingOptionInitial:当给定余额初始值的时候,自动发送通知
     */
    [bank addObserver:person forKeyPath:@"accountBalance" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial) context:nil];
    //4.需要使用KVC指定变化的值
    [bank setValue:@2000 forKey:@"accountBalance"];
    
    //移除观察者(在观察者对象和被观察者对象释放之前,移除观察者对象)
    [bank removeObserver:person forKeyPath:@"accountBalance"];

Person.m

//只要观察值发生变化,系统自动发送通知到TRPerson(调用下面的方法)
/* keyPath:观察key路径
   object:被观察者对象
   change:包含被观察对象的新值(New)/老值(Old)/初始化值(Inital)
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    //判断哪个属性发生变化两种方式(context的值;keyPath的值)
    if ([keyPath isEqual:@"accountBalance"]) {
        //观察者已经观察到余额变化(更新界面控件值)
        NSLog(@"object:%@; change New:%@; change Old:%@", object, [change objectForKey:NSKeyValueChangeNewKey], [change objectForKey:NSKeyValueChangeOldKey]);
    }
    
}

Bank.h

@interface TRBank : NSObject {
    //账户余额
    float accountBalance;
}

@end



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值