• kvo与kvc


    一、概述

    KVC 与 KVO 是 Objective C 的关键概念,个人认为必须理解的东西,下面是实例讲解。

    Key-Value Coding (KVC)

    KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。

    一个对象拥有某些属性。比如说,一个 Person 对象有一个 name 和一个 address 属性。以 KVC 说法,Person 对象分别有一个 value 对应他的 name 和 address 的 key。 key 只是一个字符串,它对应的值可以是任意类型的对象。从最基础的层次上看,KVC 有两个方法:一个是设置 key 的值,另一个是获取 key 的值。如下面的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void changeName(Person *p, NSString *newName)
    {
     
        // using the KVC accessor (getter) method
        NSString *originalName = [p valueForKey:@"name"];
     
        // using the KVC  accessor (setter) method.
        [p setValue:newName forKey:@"name"];
     
        NSLog(@"Changed %@'s name to: %@", originalName, newName);
     
    }

    现在,如果 Person 有另外一个 key 配偶(spouse),spouse 的 key 值是另一个 Person 对象,用 KVC 可以这样写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void logMarriage(Person *p)
    {
     
        // just using the accessor again, same as example above
        NSString *personsName = [p valueForKey:@"name"];
     
        // this line is different, because it is using
        // a "key path" instead of a normal "key"
        NSString *spousesName = [p valueForKeyPath:@"spouse.name"];
     
        NSLog(@"%@ is happily married to %@", personsName, spousesName);
     
    }

    key 与 key pat 要区分开来,key 可以从一个对象中获取值,而 key path 可以将多个 key 用点号 “.” 分割连接起来,比如:

    [p valueForKeyPath:@"spouse.name"];

    相当于这样……

    [[p valueForKey:@"spouse"] valueForKey:@"name"];

    好了,以上是 KVC 的基本知识,接着看看 KVO。

    KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

    KVO其实也是“观察者”设计模式的一种应用。我的看法是,这种模式有利于两个类间的解耦合,尤其是对于 业务逻辑与视图控制 这两个功能的解耦合。

    引子

    先来看个引子:

    有一个业务类:Walker,在这个类内部只负责关于业务逻辑的处理,比如负责从服务器传来的JSON中解析数据,或做其他业务数据上的处理。

    有另一个类:ViewController,专门负责界面的交互与试图更新。其中,需要讲Walker的某些属性显示出来,并实时更新。

    目前,据我所能想到的方法有以下几种:

    方法1、直接的函数调用

    在Walker的类内部,把创建一个ViewController的对象,然后调用ViewController的修改界面的方法,把需要改动的属性值作为形参传给该函数。

    这种方式最直观,因为它不需要绕任何弯子。但是,确实最糟的方法。因为Walker与ViewController这两个类从此紧紧耦合在一起了。记住这句话,处理业务逻辑的类,对外部的事情知道得越少越好。甚至于,要做到外部是否有VC(View Controller),有多少个VC都不影响我。假设这是一个项目,程序员A负责业务逻辑的处理,程序员B负责UI,则采取这种方式后,程序员A就受制于B,互相干扰。

    方法2、利用消息通信机制(NSNotification)

    在Walker内部建立消息中心NSNotificationCenter,把实例化之后的VC对象作为observer。Center建在Walker或者VC都无所谓,具体看我博客的另一篇文章【NSNotificationCenter总结】。这种方法比前面的方法好一些。但是有一个很大的缺点:如果Walker需要更改的属性很多而且很频繁,那么这种方式很不方便传值。而且,注意到了没,“把实例化后的VC对象作为observer”,始终逃不开在Walker内部对VC实例化。依旧是耦合着。

    方法3、利用delegate

    关于delegate的介绍有很多,这里就不多讲。但是在这种需求下用 delegate,有点“杀鸡用牛刀”感觉,成本较大,而且不直观。

    方法4、利用KVO模式

    所有的代码都将在ViewController中实现。对于Walker,它自己都不知道外部是否有VC,以及VC会怎用用我的属性。

    Demo

    复制代码
     1 //
     2 //  Walker.h
     3 //  KVOExample
     4 //
     5 //  Created by 老翁 on 13-7-29.
     6 //  Copyright (c) 2013年 wzl. All rights reserved.
     7 //
     8 
     9 #import <Foundation/Foundation.h>
    10 
    11 @interface Walker : NSObject
    12 {
    13     NSInteger _age;
    14     NSString *_name;
    15 }
    16 
    17 @property (nonatomic) NSInteger age;
    18 @property (nonatomic, retain) NSString *name;
    19 
    20 - (id)initWithName:(NSString *)name age:(NSInteger)age;
    21 
    22 
    23 @end
    复制代码
    复制代码
    #import "Walker.h"
    
    @implementation Walker
    
    @synthesize age = _age;
    @synthesize name = _name;
    
    - (void)dealloc
    {
        [_name release];
        [super dealloc];
    }
    
    - (id)initWithName:(NSString *)name age:(NSInteger)age
    {
        if (self = [super init]) {
            _name = name;
            _age = age;
        }
        return  self;
    }
    
    
    @end
    复制代码

    ViewController代码:

    复制代码
    //
    //  ViewController.h
    //  KVOExample
    //
    //  Created by 老翁 on 13-7-29.
    //  Copyright (c) 2013年 wzl. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    #import "Walker.h"
    
    @class Walker;
    
    @interface ViewController : UIViewController
    {
        Walker *_walker;
        UILabel *_ageLabel;
    }
    
    @property (nonatomic, assign) Walker *walker;
    
    @end
    复制代码
    复制代码
     1 //
     2 //  ViewController.m
     3 //  KVOExample
     4 //
     5 //  Created by 老翁 on 13-7-29.
     6 //  Copyright (c) 2013年 wzl. All rights reserved.
     7 //
     8 
     9 #import "ViewController.h"
    10 
    11 @interface ViewController ()
    12 
    13 @end
    14 
    15 @implementation ViewController
    16 
    17 @synthesize walker = _walker;
    18 
    19 
    20 - (void)dealloc
    21 {
    22     [_walker release];
    23     [_ageLabel release];
    24     [super dealloc];
    25 }
    26 
    27 - (void)viewDidLoad
    28 {
    29     [super viewDidLoad];
    30     // Do any additional setup after loading the view, typically from a nib.
    31     _walker = [[Walker alloc] initWithName:@"老翁" age:25];
    32     /* 关键步骤 */
    33     [_walker addObserver:self
    34               forKeyPath:@"age"
    35                  options:NSKeyValueObservingOptionNew
    36                  context:nil];
    37     
    38     
    39     //UI initialization
    40     UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    41     [btn setFrame:CGRectMake(120, 200, 100, 35)];
    42     [btn setBackgroundColor:[UIColor lightGrayColor]];
    43     [btn setTitle:@"增加5岁" forState:UIControlStateNormal];
    44     [btn addTarget:self
    45             action:@selector(buttonPressed)
    46   forControlEvents:UIControlEventTouchUpInside];
    47     [self.view addSubview:btn];
    48     
    49     _ageLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 150, 200, 35)];
    50     _ageLabel.text = [NSString stringWithFormat:@"%@现在的年龄是: %d", _walker.name, _walker.age];
    51     _ageLabel.backgroundColor = [UIColor clearColor];
    52     [self.view addSubview:_ageLabel];
    53     
    54     
    55 }
    56 
    57 - (void)buttonPressed
    58 {
    59     _walker.age += 5;
    60 }
    61 
    62 /* KVO function, 只要object的keyPath属性发生变化,就会调用此函数*/
    63 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    64 {
    65     if ([keyPath isEqualToString:@"age"] && object == _walker) {
    66         _ageLabel.text = [NSString stringWithFormat:@"%@现在的年龄是: %d", _walker.name, _walker.age];
    67     }
    68 }
    69 
    70 
    71 
    72 - (void)didReceiveMemoryWarning
    73 {
    74     [super didReceiveMemoryWarning];
    75     // Dispose of any resources that can be recreated.
    76 }
    77 
    78 @end
    复制代码
  • 相关阅读:
    熟悉常用的HBase操作
    爬虫大作业
    熟悉常用的HDFS操作
    数据结构化与保存
    获取全部校园新闻
    爬取校园新闻首页的新闻的详情,使用正则表达式,函数抽离+网络爬虫基础练习
    中文词频统计
    英语词频统计
    AXIOS中文文档
    overload方法重载
  • 原文地址:https://www.cnblogs.com/wangbinios/p/5014498.html
Copyright © 2020-2023  润新知