zoukankan      html  css  js  c++  java
  • IOS中KVO模式的解析与应用

    IOS中KVO模式的解析与应用

    最近老翁在项目中多处用到了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
    复制代码

      

    点击了按钮之后,系统会调用 observeValueForKeyPath :函数,这个函数应该也是回调函数。在该函数内部做UI更新。我们以这种轻量级的方式达到了目的。

    ##################################################

    声明 转载请注明原文地址:

    http://www.cnblogs.com/wengzilin/p/3223770.html

    欢迎私下交流学习:

    QQ 719113951

    ##################################################

     
     
    标签: KVO
  • 相关阅读:
    27 Spring Cloud Feign整合Hystrix实现容错处理
    26 Spring Cloud使用Hystrix实现容错处理
    25 Spring Cloud Hystrix缓存与合并请求
    24 Spring Cloud Hystrix资源隔离策略(线程、信号量)
    23 Spring Cloud Hystrix(熔断器)介绍及使用
    22 Spring Cloud Feign的自定义配置及使用
    21 Spring Cloud使用Feign调用服务接口
    20 Spring Cloud Ribbon配置详解
    19 Spring Cloud Ribbon自定义负载均衡策略
    18 Spring Cloud Ribbon负载均衡策略介绍
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3224156.html
Copyright © 2011-2022 走看看