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

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

  1. KVC

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

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

示例代码:

@interface ZYPerson : NSObject
@property (nonatomic, copy, readonly) NSString *name; - (instancetype)initWithName:(NSString *)name;
@end #import "ZYPerson.h" @implementation ZYPerson
- (instancetype)initWithName:(NSString *)name
{
if (self = [super init]) {
_name = name;
}
return self;
}
@end
#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怎么实现监听一个对象值的改变:

#import <Foundation/Foundation.h>

@interface ZYPerson : NSObject
@property (nonatomic, copy, readonly) NSString *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里面的代码:

#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告诉其他对象,被改变的具体情况。

先上代码:

#import <Foundation/Foundation.h>

extern NSString * const ZYAgeDidChangeNotification;

@interface ZYPerson : NSObject
@property (nonatomic, copy, readonly) NSString *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里面的代码:

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

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

  1. OC中另外的一个常用技术:通知(Notification)

    OC中另外的一个常用技术:通知(Nofitication)其实这里的通知和之前说到的KVO功能很想,也是用于监听操作的,但是和KVO不同的是,KVO只用来监听属性值的变化,这个发送监听的操作是系统控制 ...

  2. OC学习篇之---KVC和KVO操作

    前一篇文章我们介绍了OC中最常用的文件操作:http://blog.csdn.net/jiangwei0910410003/article/details/41875015,那么今天来看一下OC中的一 ...

  3. JAVA设计模式 之 观察者模式

    简介: 观察者模式是JDK中最多的设计模式之一,非常有用,观察者模式介绍了一对多的依赖关系及松耦合,有了观察者,你将会消息灵通. 认识观察者模式,看一个报纸.杂志订阅是怎么回事: (1). 报社的业务 ...

  4. iOS设计模式之观察者模式

    观察者模式 基本理解 观察者模式又叫做发布-订阅(Publish/Subscribe)模式. 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时 ...

  5. .net设计模式之观察者模式

    摘要     在今天的设计模式系列中我给大家带来了观察者模式,首先我会以一个生动的故事引入观察者模式的应用的场景,然后描述这个场景中出现的问题,最后我们提出观察者模式的解决方案,并给出C#语言实现的代 ...

  6. Android设计模式系列--观察者模式

    观察者模式,是一种非常常见的设计模式,在很多系统中随处可见,尤其是涉及到数据状态发生变化需要通知的情况下.本文以AbstractCursor为例子,展开分析.观察者模式,Observer Patter ...

  7. iOS 中KVC、KVO、NSNotification、delegate 总结及区别-b

    1.KVC,即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属性.而不是通过调用Setter.Getter方法访问.KVO 就是基于 KVC 实现的 ...

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

    设计模式之观察者模式(Observable与Observer) 好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情 ...

  9. 三种方式实现观察者模式 及 Spring中的事件编程模型

    观察者模式可以说是众多设计模式中,最容易理解的设计模式之一了,观察者模式在Spring中也随处可见,面试的时候,面试官可能会问,嘿,你既然读过Spring源码,那你说说Spring中运用的设计模式吧, ...

随机推荐

  1. linux下开启https

    1.在线安装mod_ssl yum -y install mod_ssl 查看openssl 是否安装成功 rpm -qa |grep openssl 2.建立服务器密钥 openssl genrsa ...

  2. [AaronYang原创] 敏捷开发-Jira 6.0.5环境搭建[1]

    我的环境 Win7 64位,MSSql2008 R2,已经安装tomcat了 拓展环境 jira  6.0.5     百度网盘下载           官网更多版本下载 安装好Java的运行环境(j ...

  3. appium简明教程(4)——appium client的安装

    appium client是对webdriver原生api的一些扩展和封装.它可以帮助我们更容易的写出用例,写出更好懂的用例. appium client是配合原生的webdriver来使用的,因此二 ...

  4. (LeetCode)用两个栈实现一个队列

    LeetCode上面的一道题目.原文例如以下: Implement the following operations of a queue using stacks. push(x) -- Push ...

  5. Eclipse安装PlantUML插件

    新技术的诞生和更新,新工具的发现和使用是两件让人开心的事情. 还记得Visio下苦苦的画流程图的时光吗,现在一切都变得so easy,因为有PlantUML! 官网:http://plantuml.c ...

  6. 复杂对象类型的WebService高级部分

    从客户端除了传递字符串以外还可以传递复杂对象(对象必须序列化了),List,Map,数组和文件. (1)定义一个对象实现了serializable 接口package cn.com.chenlly.s ...

  7. Nuxt 开发环境不支持ip访问?

    传送门:https://nuxtjs.org/faq/host-port 开发模式下不支持ip访问? 打开package.json,添加如下配置,然后重启即可. "config": ...

  8. java类中serialversionuid 作用 是什么?举个例子说明(转)

    serialVersionUID适用于Java的序列化机制.简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的 ...

  9. JPA之@GeneratedValue注解(转)

    JPA的@GeneratedValue注解,在JPA中,@GeneratedValue注解存在的意义主要就是为一个实体生成一个唯一标识的主键(JPA要求每一个实体Entity,必须有且只有一个主键), ...

  10. 使用C#和Thrift来访问Hbase实例

    今天试着用C#和Thrift来访问Hbase,主要参考了博客园上的这篇文章.查了Thrift,Hbase的资料,结合博客园的这篇文章,终于搞好了.期间经历了不少弯路,下面我尽量详细的记录下来,免得大家 ...