OC-KVC/KVO

设计模式中有一种模式,叫观察者模式

观察者模式:

在工程中,一些类去观察某一个类,当某一个类发生变化时,这些类就会收到相应的信息,做出相应的反应。一般当一个类需要发送消息给多个类时,就可以使用观察者模式。观察者模式可以使用一对多的消息发送。

在OC中如何实现观察者模式?

使用KVC/KVO

KVC:即键值编码,KVC提供了一种在运行时而非编译时动态访问对象属性与实例变量的方式,也就是说,我们可以使用字符串的内容作为属性名称或者实际变量名称进行访问。
KVC中基本调用包括:setValue:forKey: 和 Value:forKey: 两个方法

KVC中的基本方法 setValue:forKey: 和 Value:forKey:

//我们创建一个 Student 类,在声明文件里声明两个成员变量 _name 和 _age,并声明一个属性 address
{
    @public
    NSString *_name;
    NSUInteger _age;
}
@property (nonatomic,strong)NSString *address;
//在 Student 实现文件里重写 description 方法
- (NSString *)description{
    return [NSString stringWithFormat:@"my name is %@, %lu yesrs old , living in %@",_name,_age,_address];
}
//在 ViewController.m 文件里实现以下KVC的两个基本方法
//通过KVC赋值
//创建一个学生对象
Student *stu1 = [Student new];
[stu1 setValue:@"jimuta" forKey:@"_name"];
[stu1 setValue:@25 forKey:@"_age"];
[stu1 setValue:@"GZ" forKey:@"_address"];
NSLog(@"aa = %@",stu1);

//通过KVC访问
NSString *name = [stu1 valueForKey:@"_name"];
NSNumber *age = [stu1 valueForKey:@"_age"];
NSString *address = [stu1 valueForKey:@"_address"];

NSLog(@"name = %@ , age = %@ , address = %@",name, age, address);

使用KVC两种基本方法时要注意键搜索顺序:

setValue:forKey:方法首先查找的是 setKey 命名的 setter ,如果不存在则在对象内部查找 _key 或 key 的实例变量。Value:forKey:方法首先查找的是 key 或 isKey 命名的 getter ,如果不存在,则在对象内部查找 _key 或 key的实例变量。

KVC可以动态访问某些属性时,使用一些可以运行时而不是编译时改变的值

//我们先实现一下用编译时的方法,在Student 的声明文件里声明三个属性,并定义一个方法
@property (nonatomic,assign) int p1;
@property (nonatomic,assign) int p2;
@property (nonatomic,assign) int p3;

- (int)getValuePropertyName:(NSString *)name;
//在实现文件里面进行方法实现
- (int)getValuePropertyName:(NSString *)name{
    if ([name isEqualToString:@"p1"]) {
        return self.p1;
    }else if([name isEqualToString:@"p2"]){
        return self.p2;
    }else if ([name isEqualToString:@"p3"]){
        return self.p3;
    }
    return 0;
}
//我们通过getValuePropertyName: 这个方法,实现一个功能,即如果我输入的name与p1或p2、或p3其中一个相等,即返回对应的p值
//在ViewController.m文件里面书写
stu1.p1 = 1;
stu1.p2 = 2;
stu1.p3 = 3;
int a = [stu1 getValuePropertyName:@"p1"];
NSLog(@"a = %d",a);
//此代码输出结果为:a = 1

我们通过以上方法可以实现功能,但如果现在我们有p1~p100呢,我们不可能通过书写一百次重复的代码来实现,这时候我们就使用到了访问运行时

//在Student接口文件中声明一个新的方法
- (int)getNewValuePropertyName:(NSString *)name;
//在实现文件中实现其方法
- (int)getNewValuePropertyName:(NSString *)name{
    NSNumber *number = [self valueForKey:name];
    return [number intValue];
}
//在ViewController.m文件中实现
int b = [stu1 getNewValuePropertyName:@"p1"];
NSLog(@"b = %d",b);
//此代码运行结果为 b = 1

//我们从上面的代码对比可以看到,使用后面的方法,不仅减少了代码行数,而且可拓展性更强、更灵活,只要向声明文件中添加相应的属性,无需对方法做任何修改就可以做到通过属性名称访问属性的值

键路径编码
对于对象中实例变量或属性是另外一对象的情况,可以使用键路径编码
假设 a 对象下有个 B 类型的属性 b,而 b 对象中包含了 C 类型的属性 c,如果想要从 a 获取(或设置) c 属性的内容,需要使用如下路径来描述: b.c
注意:使用键路径时要用 setValue:forKeyPath: 和 Value:forKeyPath: 方法

//我们创建一个新的类,Book,在实现文件中声明一个成员变量_bookName;
{
    NSString *_bookName;
}

//在 Student 类中导入 Book 的头文件,并声明一个成员变量
{
    Book *_book;
}
//在viewControllar.m文件中实现
//创建book对象
Book *book = [Book new];

[stu1 setValue:book forKeyPath:@"_book"];

[stu1 setValue:@"论如何沉迷IOS" forKeyPath:@"_book._bookName"];

NSString *bookName = [stu1 valueForKeyPath:@"_book._bookName"];
NSLog(@"bookName = %@",bookName);
//输出结果为;bookName = 论如何沉迷IOS

KVO的由来:
在编程的过程中,我们经常需要判断目标是否发生变化,以便及时的做出对应的处理。此时苹果公司就提供了一种策略,即’OC运行时’提供了’KVO’技术,其中’KVO’是基于’KVC’实现。

KVO的实现:
1.注册成为观察者
2.观察者定义KVO的回调
3.移除观察者

//创建一个类 Observer ,在声明文件中定义一个方法
- (id)initWithHero:(Hero *)hero;
- (id)initWithHero:(Hero *)hero{
    if (self = [super init]) {
        //注册成为观察者
        self.hero = hero;
        [self.hero addObserver:self
                    forKeyPath:@"_HP"
                       options:NSKeyValueObservingOptionNew
                       context:nil];
        /*
         1.表示接受通知的对象,即观察者,通常为self
         2.键路径参数,要观察的键路径
         3.标志KVO希望变化如何传递给观察者,这些值可以使用 | 进行多选
         4.上下文内存区,通常为nil
         */
    }
    return self;
}
//观察者定义KVO的回调
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context{

    NSLog(@"%@",[change objectForKey:NSKeyValueChangeNewKey]);
}
//移除观察者
- (void)dealloc{
    [self.hero removeObserver:self forKeyPath:@"_HP"];
    /*
    1.移除的观察者对象
    2.观察的键路径
    */
}
//创建一个hero、observer的对象
Hero *hero = [Hero new];
Observer *observer = [[Observer alloc] initWithHero:hero];
[hero setValue:@300 forKey:@"_HP"];
//实现obsever对hero里面HP的观察
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值