当前位置: 首页 > news >正文

【原】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

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

相关文章:

  • Lucene的多域查询、结果中查询、查询结果分页、高亮查询结果和结果评分
  • 工具篇:产品经理常用的工具
  • ASP.Net上传中文文件乱码
  • 配置管理小报091119: waiting for abc's lock in /db1/cp/src
  • SVN服务器搭建和使用
  • 怎样判断自己是否在平庸者之列?
  • 通向云之路:云计算的主要优势
  • hibernate动态创建表, 动态加载映射
  • java多线程synchronized,wait,notify的大致用法
  • linux知识汇总
  • 自己做的笔试题
  • 风斗
  • localhost:3000显示rails首页的波折
  • 原型模式 objective-c 版
  • 数数http://acm.nyist.net/JudgeOnline/problem.php?pid=198
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • CAP 一致性协议及应用解析
  • EOS是什么
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JS笔记四:作用域、变量(函数)提升
  • leetcode386. Lexicographical Numbers
  • mysql 数据库四种事务隔离级别
  • python3 使用 asyncio 代替线程
  • ubuntu 下nginx安装 并支持https协议
  • 关于List、List?、ListObject的区别
  • 关于springcloud Gateway中的限流
  • 机器学习中为什么要做归一化normalization
  • 聊一聊前端的监控
  • 微信小程序--------语音识别(前端自己也能玩)
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 项目实战-Api的解决方案
  • 一个JAVA程序员成长之路分享
  • 移动端 h5开发相关内容总结(三)
  • ​2021半年盘点,不想你错过的重磅新书
  • "无招胜有招"nbsp;史上最全的互…
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • $refs 、$nextTic、动态组件、name的使用
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (二)WCF的Binding模型
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • .gitignore文件---让git自动忽略指定文件
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET Core中的去虚
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .NET中的Exception处理(C#)
  • [Android] Amazon 的 android 音视频开发文档
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [bzoj1038][ZJOI2008]瞭望塔
  • [C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)
  • [CareerCup] 12.3 Test Move Method in a Chess Game 测试象棋游戏中的移动方法
  • [ChromeApp]指南!让你的谷歌浏览器好用十倍!
  • [Docker]十二.Docker consul集群搭建、微服务部署,Consul集群+Swarm集群部署微服务实战
  • [EFI]DELL XPS13 9360电脑 Hackintosh 黑苹果efi引导文件