iOS开发常用设计模式之KVO模式

    最近有点忙,设计产品功能,改官网,这一闲下来就突然迷茫了,不知道该干点什么,到今天已经一个月没有更新博客了,真是不应该,坚持更新博客是一种记录生活的方式。
    今天主要讲的是iOS中经典的设计模式-KVO模式。KVO提供了一种机制,指定一个被观察对象,当这个对象的某个属性发生变化的时候,这个对象会获得属性变化的通知,以作出相应的处理。KVO不管是在面试还是工作中我们都会被用到这个东西,今天我将用一个demo通过OC与Swift两种语言来讲述简单的应用KVO,我的邮箱是KenenCS@163.com,欢迎多多交流!

KVODemo


demo效果:
    点击增加按钮时候num值加1,
    点击重置按钮时候num值归0,
    点击减少按钮时候num值减1;

KVO分三步走:
    1.注册观察者;
    2.在观察的属性变化的方法里面做处理;
    3.移除KVO;

一、使用OC语言创建KVO观察者模式


准备工作:
    我们建一个新工程,名字叫做TestDemo。创建完之后通过Cocoa Touch Class添加一个继承于NSObject的类,起名叫做myKVO,作为我们要用的观察者对象;

准备工作


操作我们新建的myKVO类

myKVO.h

1
2
3
4
5
6
7
#import <Foundation/Foundation.h>
@interface myKVO : NSObject
//创建一个属性我们一会来监听它的变化
@property (nonatomic,assign)int num;
@end

myKVO.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import "myKVO.h"
#import <UIKit/UIKit.h>
@implementation myKVO
//合成器与声明num的property相对应,setter和getter方法
@synthesize num;
//重写了num的setter方法,因为我们有个要求是设置num数字范围在0-100之内
- (void)setNum:(int)newNum {
if (newNum>100||newNum<0) {
NSLog(@"超界了");
}else {//正常赋值
num = newNum;
}
}
//num的getter方法
- (int)num {
return num;
}
@end


操作我们ViewController

viewController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我这里采用XIB拖拽的形式
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
//展示的文字
@property (weak, nonatomic) IBOutlet UILabel *label;
//增加按钮
@property (weak, nonatomic) IBOutlet UIButton *changeNum;
//减少按钮
@property (weak, nonatomic) IBOutlet UIButton *jianShao;
//重置按钮
@property (weak, nonatomic) IBOutlet UIButton *chongZhi;
@end

viewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#import "ViewController.h"
#import "myKVO.h"
@interface ViewController ()
//我们需要观察的对象
@property (nonatomic,strong)myKVO *mykvo;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//初始化观察者对象
self.mykvo = [[myKVO alloc] init];
/*
注册观察者的代码
-----------参数简介----------
*observer 观察者;
*KeyPath 被观察的属性的名称;
*options 被观察属性的一些配置,这里使用的,NSKeyValueObservingOptionOld代表观察旧值,NSKeyValueObservingOptionNew代表观察新值;
*context 上下文,可以给kvo的回调方法传值;
*/
[self.mykvo addObserver:self forKeyPath:@"num" options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:nil];
}
/*
只要你观察的keypath发生变化就会调用这个方法
----------------参数简介--------------
*keypath 被观察的属性的名称;
*object 被观察的对象;
*change 前后变化的值都是储存在这个字典中的;
*context 注册观察者时,context传过来的值;
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"num"] && object==self.mykvo) {
// 这里使用KVO就是为了改变label上的数字
self.label.text = [NSString stringWithFormat:@"当前的num值是:%@",[change valueForKey:@"new"]];
// [change valueForKey:@"new"],代表新值,对应注册观察者时的NSKeyValueObservingOptionNew
// [change valueForKey:@"old"],代表旧值,对应注册观察者时的NSKeyValueObservingOptionOld
NSLog(@"旧值:%@ ----- 新值:%@ -----",[change valueForKey:@"old"],[change valueForKey:@"new"]);
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//增加按钮点击事件
- (IBAction)changeNum:(UIButton *)sender {
self.mykvo.num = self.mykvo.num+1;
}
//减少按钮点击事件
- (IBAction)jianShao:(UIButton *)sender {
self.mykvo.num = self.mykvo.num-1;
}
//重置按钮点击事件
- (IBAction)chongZhi:(UIButton *)sender {
self.mykvo.num = 0;
}
//移除kvo
-(void)dealloc {
[self removeObserver:self forKeyPath:@"num"];
}
@end


一、使用Swift语言创建KVO观察者模式


    使用Swift语言是真的简洁,我们只需要创建一个Swift工程就行了,别忘了在选择语言的时候是Swift而不是Object-C哦!我们只需要在你新建的工程中的viewController.swift文件操作即可;

操作我们观察者对象myKVO

    因为我们操作的myKVO对象中的属性num的时候,需要用到num的setter和getter方法,但是Swift中的setter和getter与OC中的可是不一样的,质一样区别,所以我感觉有必要先给大家普及一下Swift中setter和getter方法:

Swift中的属性分为两种属性,一种就是计算型属性 一种就是存储型属性.

1.计算型属性是什么?
    计算型属性是通过计算而得出来的属性,这种属性相区别于存储属性这种属性是不会存储的。如果是计算型属性,那么提供setter方法那就一定需要提供getter方法,可以直接只有一个getter方法,其实仔细想一想这样的设计是有缘由的,计算型属性我们是为了得出什么?我们是为了获取计算出来的值,那么你提供了setter方法,不提提供getter方法(他又不会存储)那么你是不能得到想要的值的,setter方法他会将新值保存在一个叫newValue中,我们可以直接用,当然getter中也有一个newValue。
2.存储型属性是什么?
    存储型属性就是一个需要存储的属性,如果我们需要自定义setter和getter方法,我们得注意一下,setter方法存在两种,willSet和didSet,这两种方法我们不一定都需要实现,根据需求实现各自的方法,willSet是将要赋值的时候调用的,而didSet方法是已经赋完了值之后调用的。可以提供他的getter方法,和计算型属性不一样的是,他可以有setter方法没有getter,想想设计也是相当的合理,既然他是存储型的属性,已经存起来了,那么我们可以取得到。在willSet方法里没有必要赋值,除非你要改变新赋的值,getter方法和setter 方法不能同时出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import UIKit
//被观察的对象
class myKVO: NSObject {
//我们要用它代替num
var _num:Int = 0;
//使用 @dynamic 修饰,表示该属性的存取都由 runtime 在运行时来决定,由于 Swift 基于效率的考量默认禁止了动态派发机制,因此要加上该修饰符来开启动态派发,而OC中是默认开启动态派发机制;
dynamic var num:Int{
//我们此次使用的是计算型属性
//重写num的setter方法,设置界限在0-100之内
set {
if (_num>100||_num<0) {
print("超界了")
}
//newValue是系统默认的
_num = newValue;
}
//重写num的getter方法
get {
return _num;
}
}
}


操作我们viewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//控制器
class ViewController: UIViewController {
//展示的文字
@IBOutlet weak var label: UILabel!
//增加按钮
@IBOutlet weak var changeNum: UIButton!
//重置按钮
@IBOutlet weak var chongZhi: UIButton!
//减少按钮
@IBOutlet weak var jianShao: UIButton!
//创建观察者对象
var myKvo:myKVO = myKVO();
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
/*
注册观察者的代码
-----------参数简介----------
*observer 观察者;
*KeyPath 被观察的属性的名称;
*options 被观察属性的一些配置,这里使用的,NSKeyValueObservingOption.Old代表观察旧值,NSKeyValueObservingOption.New代表观察新值,NSKeyValueObservingOption.indexes代表最初的值;
*context 上下文,可以给kvo的回调方法传值;
*/
//NSKeyValueObservingOption.New可以简写成.new
self.myKvo.addObserver(self, forKeyPath: "num", options: .new, context: nil);
}
/*
只要你观察的keypath发生变化就会调用这个方法
----------------参数简介--------------
*keypath 被观察的属性的名称;
*object 被观察的对象;
*change 前后变化的值都是储存在这个字典中的;
*context 注册观察者时,context传过来的值;
*/
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath=="num" {
/*
这里使用KVO就是为了改变label上的数字
*.newKey,代表新值,对应注册观察者时的NSKeyValueObservingOption.New
*.oldKey,代表旧值,对应注册观察者时的NSKeyValueObservingOption.Old
*.indexesKey,代表最初的值,对应注册观察者时的NSKeyValueObservingOption.indexes
*/
if let newValue = change?[.newKey] {
self.label.text = "当前的num值是:\(newValue)";
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//增加按钮点击事件
@IBAction func changeNum(_ sender: UIButton) {
self.myKvo.num = self.myKvo.num+1;
}
//重置按钮点击事件
@IBAction func chongZhi(_ sender: UIButton) {
self.myKvo.num = 0;
}
//减少按钮点击事件
@IBAction func jianShao(_ sender: UIButton) {
self.myKvo.num = self.myKvo.num-1;
}
//移除KVO
deinit {
self.removeObserver(self, forKeyPath: "num");
}
}



以上就是此次讲解,欢迎大家下载交流!Demo地址如下:
Swift版本:KVOSwiftDemo
OC版本:KVODemo

文章目录
  1. 1. 一、使用OC语言创建KVO观察者模式
    1. 1.1. myKVO.h
    2. 1.2. myKVO.m
    3. 1.3. viewController.h
    4. 1.4. viewController.m
  2. 2. 一、使用Swift语言创建KVO观察者模式
|