到目前为止,可以通过存取方法,属性或直接设置实例变量来直接更改对象状态。也可以通过字符串描述来更改对象状态,这种方式称为键值编码(KVO)。KVC提供了一种运行时而非编译时动态访问对象属性与实例变量的方式。
KVC的基本用法
setValue:(要设置的值)forKey:(键,对象的属性名称或者实例变量名称),用来设置实例变量的值。
valueForKey:(对象的属性名称或者实例变量名称),用于获取实例变量的值。
注意键的搜索顺序,setValue:ForKey:方法首先查找的是settee命名的setter,如果不存在则在对象内部查找_key或key的实例变量。valueForKey:方法首先查找的是key或isKey命名的getter,如果不存在则在对象内部查找_key或key的实例变量。
KVC可以编写出动态性更强的代码,并减少需要表示对象属性信息的代码。
创建一个Student类:
@interface Student:NSObject
{
NSString *_name;
NSUInteger _age;
Book *_book;//创建一个Book类型的成员变量
}
@property (nonatomic,strong)NSString *address;
@property (nonatomic,assign)int p1;
@property (nonatomic,assign)int p2;
@property (nonatomic,assign)int p3;
-(int)getValuePropertyName:(NSString *)name;
-(int)getNewValuePropertyName:(NSString *)name;
@implementation Student
//重写description方法
-(NSString *)description{
//返回一个字符串
return [NSString stringWithFormat:@"My name is %@,%lu years old,living in %@",_name,_age,_address];
}
//接收一个字符串参数,返回int类型的数据
-(int)getValuePropertyName:(NSString *)name{
//注意加中括号[],返回的是BOOL类型的值
if([name isEqualToString:@"p1"]){
return _p1;
}
if([name isEqualToString:@"p2"]){
return _p2;
}
if([name isEqualToString:@"p3"]){
return _p3;
}
return 0;//表示结束
}
-(int)getNewValuePropertyName:(NSString *)name{
//self---当前对象的指针
NSNumber *number = [self valueForKey:name];
//返回int类型的数据
return [number intValue];
}
使用 setValue: forKey: 设置stu1的键值
Student *stu1 = [Student new];
[stu1 setValue:@"Rick" forKey:@"_name"];
[stu1 setValue:@25 forKey:@"_age"];
[stu1 setValue:@"GZ" forKey:@"address"];
NSLog(@"%@",stu1);
//运行结果:My name is Rick,25 years old,living in GZ
使用 valueForKey: 访问键对应的值
NSString *name = [stu1 valueForKey:@"_name"];
//读取的是对象类型的数据,用NSNumber类型的age接收
NSNumber *age = [stu1 valueForKey:@"_age"];
NSString *address = [stu1 valueForKey:@"_address"];
NSLog(@"name = %@,age = %@,address = %@",name,age,address);
动态访问某些属性时,使用一些可以在运行时而不是编译时改变其值
stu1.p1 = 1;
stu1.p2 = 2;
stu1.p3 = 3;
int a = [stu1 getValuePropertyName:@"p1"];
NSLog(@"%d",a);
//此种方式比较灵活,即运行时的好处
int b = [stu1 getNewValuePropertyName:@"p1"];
NSLog(@"%d",b);
//Book为继承于NSObject的类,里面有一个_bookName成员变量
Book *book = [Bood new];
//键路径编码,stu1调用setValue:forKeyPath:
[stu1 setValue:book forKeyPath:@"_book"];//把book对象赋值给_book键
[stu1 setValue:@"葵花宝典,欲练神功。。。。" forKeyPath:@"_book._bookName"];
NSString *bookName = [stu1 valueForKeyPath:@"_book._bookName"];
NSLog(@"bookName = %@",bookName);
KVO
KVO的概念及用法如下:
/*
设计模式--观察模式
设计模式用来解决某一特定问题
观察者模式
什么是观察者模式?
在工程中,一些类去观察‘A’类,当A类发生变化的时候,这些类就会收到消息,作出相应的反应。
什么时候去使用观察者模式?
当一个类需要发送消息给多个类的时候,就可以使用观察者模式。
观察者模式有什么作用?
一对多的消息发送。
在OC中如何实现观察者模式?
OC中观察者模式的设计基础 KVC/KVO
KVC(键值编码 Key-Value-Coding)
KVC提供了一种在运行时而非编译时动态访问对象属性与实例变量的方式
*/
在编码的过程中,我们经常需要判断目标是否发生改变,以便及时做出对应的处理。这时就可以使用到KVO设计模式。
**编写键值观察分为三步进行:
1.注册成为观察者
2.观察者定义KVO的回调
3.移除观察者**
英雄类:
@interface Hero : NSObject
{
NSUInteger _HP;
}
观察者类:
#import <Foundation/Foundation.h>
#import "Hero.h"
@interface Observer : NSObject
@property (nonatomic,strong)Hero *hero;
- (id)initWithHero:(Hero *)hero;
//以下是实现
@implementation Observer
- (id)initWithHero:(Hero *)hero{
if (self = [super init]) {
self.hero = hero;
//1.注册成为观察者
/*
四个参数的含义如下:
1.表示接受通知的对象,即观察者,通常是self
2.键路径参数,要观察的路径
3.标识KVO希望变化如何传递给观察者,这些值可以使用‘|’进行多选。
4.上下文内存区,通常为nil
*/
[self.hero addObserver:self forKeyPath:@"_HP" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
//2.定义回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
NSLog(@"%@",[change objectForKey:NSKeyValueChangeNewKey]);//注意字典取两个Key操作,如:dic[@"name"],dic[@"age"]
}
//3.移除观察者
-(void)dealloc{
NSLog(@"我挂了。。");
/*
2个参数的含义:
1.移除的观察者对象
2.观察者的键路径
*/
[self.hero removeObserver:self forKeyPath:@"_HP"];
}
在主函数中:
@interface ViewController ()
{
Hero *_hero;//全局变量
Observer *_observer;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/*
KVO的由来:
在编程过程中,我们经常需要判断目标是否发生变化,以便及时的做出相应的处理。此时苹果公司就提供了一种策略,即‘OC运行时’提供了‘KVO’技术。其中‘KVO’是基于‘KVC’实现
KCO的实现
1.注册成为观察者
2.观察者定义KVO回调
3.移除观察者
*/
Hero *hero = [Hero new];//局部变量,该方法之外不能用
[hero setValue:@100 forKey:@"_HP"];
//observer观察 hero 的 HP 的变化
Observer *observer = [[Observer alloc] initWithHero:hero];
[hero setValue:@300 forKey:@"_HP"];
//计时器的5个参数
// 1.时间间隔
// 2.
// 3.
// 4.
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(Time:) userInfo:nil repeats:YES];
_hero = hero;
_observer = observer;//定义全局变量,调用observer
}
//为什么没有运行环境都可以运行??--singleView默认添加
- (void)Time:(NSTimer *)timer{
NSUInteger _curHP = [[_hero valueForKey:@"_HP"] integerValue];//KVC设值
_curHP -= 50;//每隔一秒减少50
if (_curHP <= 0) {
NSLog(@"英雄挂了");
[_hero setValue:@0 forKey:@"_HP"];
[timer invalidate];//使无效,停止
}
else{//
[_hero setValue:@(_curHP) forKey:@"_HP"];
}
}