设计模式之观察者模式(关于OC中的KVO\KVC\NSNotification)

学习了这么久的设计模式方面的知识,最大的感触就是,设计模式不能脱离语言特性。近段时间所看的两本书籍,《大话设计模式》里面的代码是C#写的,有一些设计模式实现起来也是采用了C#的语言特性(C#的API,抽象类,在OC中是没有抽象类、没有多继承关系),《设计模式之禅》里面的代码是JAVA写的,与OC差距也是比较大。

但是我想,这些都不是问题,学习设计模式主要学习的是其中的思想,并将之改造成自己所熟悉语言的模式,大同小异。所需要注意的是,在学习的过程中,将之与语言结合起来,多思考、多实践。

  1. KVC

KVC:  key values coding   键值编码,间接通过字符串对应的key取出、修改其对应的属性。

作用: 可以访问和修改私有成员变量、readOnly成员变量的值。(替换系统自带的导航栏、替换系统自带的Tabbar等)

示例代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@interface ZYPerson
NSObject

@property (nonatomiccopyreadonlyNSString *name;

 

-
(instancetype)initWithName:(
NSString *)name;

@end

 

 

#import
"ZYPerson.h"

 

@implementation ZYPerson

-
(instancetype)initWithName:(
NSString *)name

{

    if (self =
[
super init])
{

        _name
= name;

    }

    return self;

}

@end

 


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

#import
"ViewController.h"

#import
"ZYPerson.h"

@interface ViewController
()

 

@end

 

@implementation ViewController

 

-
(
void)viewDidLoad
{

    [super viewDidLoad];

    //
Do any additional setup after loading the view, typically from a nib.

     

    ZYPerson
*personOne = [[ZYPerson alloc] initWithName:@
"张三"];

     

    NSLog(@"%@",personOne.name);

     

     

    // 
然后,我发现名字写错了,需要修改

//   
personOne.name = @"王五";       //  如果这么写,发现编译器报错,报错很正常,我写的是readOnly

     

    // 
那么,在不改变原来代码的结构上,如何修改?在这里,KVO就有用处了

     

    [personOne
setValue:@
"王五" forKeyPath:@"name"];

     

    NSLog(@"%@",personOne.name);

}

 

 

@end

 这仅仅只是一个示例,KVC当然是强大的,UIKit框架里面很多属性是readOnly、私有的,往往我们在开发中会觉得有一些属性不好用,想改变吧,要么是readOnly,要是是私有的,难道重新写一套?但是耗时耗力,项目需要赶进度的话,就得加班。这个时候,KVC的作用就大了,我们可以自定义那些特定需求的控件,然后用KVC将系统自带的换掉,换成自定义的,简单快速轻松就可以搞定了。当然,要是系统没有对应属性的控件,就只能自定义了。

 

2. KVO

KVO是用来做属性监听的,用完后必须要移除它。

其实现原理:KVO是基于runtime机制实现的,当某个类的对象第一次被观察时,系统就会在运行期动态的创建一个该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法,派生类在重写基类的setter方法中实现真正的通知机制。

如此,来看看代码里面KVO怎么实现监听一个对象值的改变:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#import
<Foundation/Foundation.h>

 

@interface ZYPerson
NSObject

@property (nonatomiccopyreadonlyNSString *name;

@property (nonatomic,
assign) 
int age;

 

-
(instancetype)initWithName:(
NSString *)name;

@end

 

 

#import
"ZYPerson.h"

 

@implementation ZYPerson

-
(instancetype)initWithName:(
NSString *)name

{

    if (self =
[
super init])
{

        _name
= name;

    }

    return self;

}

@end

 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

#import
"ViewController.h"

#import
"ZYPerson.h"

@interface ViewController
()

@property (nonatomic,
strong) ZYPerson *personOne;

@end

 

@implementation ViewController

 

-
(
void)viewDidLoad
{

    [super viewDidLoad];

    //
Do any additional setup after loading the view, typically from a nib.

     

    self.personOne
= [[ZYPerson alloc] initWithName:@
"张三"];

    self.personOne.age
= 10;

     

    // 
personOne添加一个监听器,监听age属性的变化,options  是属性怎么样的变化

    [self.personOne
addObserver:
self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

     

    //当属性变化了,会调用observeValueForKeyPath方法

    self.personOne.age
= 20;

     

     

}

 

-
(
void)dealloc

{

    //必须要移除监听器

    [self.personOne
removeObserver:
self forKeyPath:@"age"];

}

 

/**

 
当被监听属性发生改变的时候,会调用此方法

 *

 
@param keyPath 属性名

 
@param object  属性所属的对象

 
@param change  属性的修改情况

 
@param context

 */

-
(
void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(
id)object
change:(
NSDictionary<NSString *,id>
*)change context:(
void *)context

{

    // 
当你在controller中添加多个KVO时,所有的回调都是走这个方法,那就必须对触发回调函数的来源进行判断

    if (object
== 
self.personOne
&& [keyPath isEqualToString:@
"age"])
{

        [self doSomethingWhenContextDidChanged];

    }

    else{

        /**

         
我们假设当前类还有父类,并且父类也有自己绑定了一些其他KVO呢?我们看到,这个回调函数体中只有一个判断,如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的superClass,或者super-superClass...中

         */

        [super observeValueForKeyPath:keyPath
ofObject:object change:change context:context];

    }

}

 

-
(
void)doSomethingWhenContextDidChanged

{

    NSLog(@"doSomethingWhenContextDidChanged");

}

@end

 上述,就是一个KVO的完整实现,但事实上,还是有瑕疵的,潜在的问题有可能出现在dealloc中对KVO的注销上。KVO的一种缺陷(其实不能称为缺陷,应该称为特性)是,当对同一个keypath进行两次removeObserver时会导致程序crash,这种情况常常出现在父类有一个kvo,父类在dealloc中remove了一次,子类又remove了一次的情况下。

3. NSNotification

一个类的属性发生改变,我们也可以使用NSNotification告诉其他对象,被改变的具体情况。

先上代码:


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

#import
<Foundation/Foundation.h>

 

extern NSString const ZYAgeDidChangeNotification;

 

@interface ZYPerson
NSObject

@property (nonatomiccopyreadonlyNSString *name;

@property (nonatomic,
assign) 
int age;

 

-
(instancetype)initWithName:(
NSString *)name;

@end

 

 

#import
"ZYPerson.h"

 

NSString const ZYAgeDidChangeNotification
= @
"ZYAgeDidChangeNotification";

 

@implementation ZYPerson

-
(instancetype)initWithName:(
NSString *)name

{

    if (self =
[
super init])
{

        _name
= name;

    }

    return self;

}

 

//重写age的setter方法,在这里发送age被更改的notification

 

-
(
void)setAge:(int)age

{

    _age
= age;

     

    [[NSNotificationCenter defaultCenter]
postNotificationName:ZYAgeDidChangeNotification object:
nil userInfo:nil];

}

@end

 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

#import
"ViewController.h"

#import
"ZYPerson.h"

@interface ViewController
()

@property (nonatomic,
strong) ZYPerson *personOne;

@end

 

@implementation ViewController

 

-
(
void)viewDidLoad
{

    [super viewDidLoad];

    //
Do any additional setup after loading the view, typically from a nib.

     

    // 
接受消息

    [[NSNotificationCenter defaultCenter]
addObserver:
self selector:@selector(doSomethingWhenContextDidChanged)
name:ZYAgeDidChangeNotification object:
nil];

     

    self.personOne
= [[ZYPerson alloc] initWithName:@
"张三"];

    self.personOne.age
= 10;

     

    //当属性变化了,会调用observeValueForKeyPath方法

    self.personOne.age
= 20;

     

}

 

-
(
void)dealloc

{

    [[NSNotificationCenter defaultCenter]
removeObserver:
self];

}

 

-
(
void)doSomethingWhenContextDidChanged

{

    NSLog(@"doSomethingWhenContextDidChanged");

}

@end

 这样,也是可以监听到对象属性的改变的,甚至,我们在用delegate来监控一些状态的改变也是可以做到的,这些都可以说是OC中的监听者模式。

只是说,需要注意,如果是跨控制器之间的监听、或者传递信息,建议用NSNotification更好,如果是view与它的ViewController之间的监听,用委托(也就是delegate)更好。

 

4. 观察者模式

KVO、NSNotification、委托都可以说是OC里面的监听者模式,NSNotification更重量级一些,除了监听外,还需负责传递信息等。

什么时候使用观察者模式:

    1. 有两种抽象类型相互依赖,将他们封装在各自的对象中,就可以对它们单独进行改变和复用。
    2. 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
    3. 一个对象必须通知其他对象,而它又不知道其他对象是什么。

MVC是由各种复杂的设计模式组合而成的复合结构,观察者是其中的设计模式之一。视图与控制器联系在一起,等待会影响应用程序表现的事件发生。例如,当用户单击视图上的排序按钮时,事件会传递给控制器,模型在后台排序完毕后,会通知所有相关的控制器,让它们用新的数据更新视图。

在MVC中使用观察者模式,每个组件都能够被独立复用与扩展,而对关系中的其他组件没有太多干扰。所得到的高度可复用性与可扩展性,是把其全部逻辑放入一个类中所无法得到的。因此,向控制器添加额外的视图时,不用修改已有的设计和代码。同样,不同的控制器可以使用同一个模型,而不用对使用它们的其他控制器做修改。

 

朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。

时间: 2025-01-01 02:55:57

设计模式之观察者模式(关于OC中的KVO\KVC\NSNotification)的相关文章

设计模式之观察者模式

1.有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己.在oc中也常用到这种设计模式:KVO.通知. 2.模式中主要包括四部分:抽象主题.具体主题.抽象观察者.具体观察者 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者.抽象主题提供一个接口,可以增加和删除观察者对象. 具体主题(ConcreteSubject):将

PHP设计模式之观察者模式示例介绍

 这篇文章主要介绍了PHP设计模式之观察者模式(Observer)详细介绍和代码实例,需要的朋友可以参考下 [意图]   定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新[GOF95] 又称为发布-订阅(Publish-Subscribe)模式.模型-视图(Model-View)模式.源-监听(Source-Listener)模式.或从属者(Dependents)模式   [观察者模式结构图]       [观察者模式中主要角色]   1.抽

PHP设计模式之观察者模式实例_php实例

首先了解观察者模式的概念:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察.当可观察的对象更改时,它会将消息发送到已注册的观察者.这些观察者使用该信息执行的操作与可观察的对象无关.结果是对象可以相互对话,而不必了解原因.观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦. UML结构图: 观察者模式解决的问题 在我们的开发过程中,应该

OC中什么是单例模式,,求解答

问题描述 OC中什么是单例模式,,求解答 能不能通俗易懂的讲一下什么是单例模式,单例模式有什么作用? 最好有个简单的小例子 解决方案 简单来说,一个程序中这样的对象只有一个,并且只能有一个. 比如说你程序中的当前用户配置,全局变量,系统中唯一的硬件和网络连接等等. 解决方案二: 例子:http://blog.sina.com.cn/s/blog_7c452219010148jo.html 解决方案三: 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可

iOS设计模式之观察者模式

观察者模式 基本理解 观察者模式又叫做发布-订阅(Publish/Subscribe)模式. 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,是他们能够自动更新自己. 观察者只从通知器(发行商)把自己注册到(订阅)特定的通知(杂志).当有通知的时候,观察者从通知器得到它订阅的通知. 观察者模式的特点 将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性.我们不希望为了维护一致性而

Python设计模式之观察者模式实例_python

关于设计模式中的观察者模式,定义如下(维基百科): 觀察者模式(有時又被稱為發布/訂閱模式)是軟體設計模式的一種.在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知.這通常透過呼叫各觀察者所提供的方法來實現.此種模式通常被用來實作事件處理系統.简单来说,一个被观察者有很多观察者,被观察者的状态的改变会引起所有观察者的响应操作. 那么我们用Python2.7来实现观察者模式. Python中的集合set 集合(set),类似于列表(list),但是它没有重

java设计模式之观察者模式_java

        观察者模式又称发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己.将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性.我们不希望为了维持一致性而使各类紧密耦合,这样会给维护.扩展和复用都带来不便.观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体. 观察者模式是实际中

设计模式之观察者模式(Observable与Observer)

好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情形:有A.B.C.D等四个独立的对象,其中B.C.D这三个对象想在A对象发生改变的第一时间知道这种改变,以便做出相应的响应或者对策. 上面的这种情形,就是观察者模式. 当然可以有多个观察者,多个被观察者. 观察者与被观察者也不是对立的,一个对象可以观察其他对象,也可以被其他对象观察. 2.观察者模式的应用 为了更好的理解什么是观察者模式,下面我举一些可能用到该模式的情形或

ios-新人请教OC中self用法

问题描述 新人请教OC中self用法 新人小白,求教下面的获取文本框.标签的文本属性为什么都要用self.num1.text,self.resultLabel.text, 不能直接用num1.text,resultLabel.text??? #import "HMViewController.h" @interface HMViewController () @property (nonatomic, weak) IBOutlet UITextField *num1; @propert