「观察者(Observer)」设计模式 Swift实现

这里写目录标题

  • 介绍
    • 设计模式介绍
    • 举例
  • iOS 中已有的 观察者设计模式实现
    • Notification
      • 什么是通知机制或者说如何实现通知机制?
    • KVO
      • KVO底层实现
      • 如何实现手动KVO?

介绍

设计模式介绍

观察者设计模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会受到通知并自动更新。

在这种模式中,被观察者对象(Subject)存储其观察者对象(Observer)列表,并提供了用于添加和删除观察者对象的方法。而观察者对象则实现了一个更新方法来响应被观察者状态的变化。

举例

下面我们用一个简单的 Swift 代码例子来阐述观察者设计模式的应用。

假设我们有一个带有多个温度传感器的系统,并且我们需要监控这些传感器的温度变化。我们可以使用观察者模式来实现:

首先,我们定义一个 Subject 协议来规范被观察者对象的行为:

protocol Subject {func addObserver(observer: Observer)func removeObserver(observer: Observer)func notifyObservers()
}

在这里,我们定义了 addObserver()、removeObserver() 和 notifyObservers() 方法,分别用于添加、删除观察者和通知观察者。

然后,我们定义一个 Observer 协议来规范观察者对象的行为:

protocol Observer {func update(temperature: Double)
}

在 Observer 中,我们定义了一个 update() 方法,用于响应被观察者状态的变化。

最后,我们定义一个 WeatherStation 对象,实现 Subject 协议。在 WeatherStation 中,我们维护了一个 observers 数组,用于存储所有观察者对象,并在温度变化时调用 notifyObservers() 方法通知所有观察者:

class WeatherStation: Subject {var temperature: Double = 0var observers: [Observer] = []func addObserver(observer: Observer) {observers.append(observer)}func removeObserver(observer: Observer) {if let index = observers.firstIndex(where: { $0 === observer }) {observers.remove(at: index)}}func notifyObservers() {for observer in observers {observer.update(temperature: temperature)}}func setTemperature(temperature: Double) {self.temperature = temperaturenotifyObservers()}
}

在 WeatherStation 中,我们使用 addObserver() 方法将观察者对象添加到 observers 数组中,使用 removeObserver() 方法将观察者对象从 observers 数组中删除。而 setTemperature() 方法用于设置温度值,并在温度变化时通知所有观察者。

最后,我们定义一个 Display 作为观察者对象,实现 Observer 协议。在 Display 中,我们实现了 update() 方法,在被观察者状态改变时更新显示:

class Display: Observer {func update(temperature: Double) {print("当前温度为:\(temperature)")}
}

在客户端代码中,我们可以实例化一个 WeatherStation 对象,并添加多个 Display 观察者对象,然后设置温度值,从而触发所有观察者对象的更新:

let weatherStation = WeatherStation()
let display1 = Display()
let display2 = Display()weatherStation.addObserver(observer: display1)
weatherStation.addObserver(observer: display2)weatherStation.setTemperature(temperature: 25)

输出结果:

当前温度为:25.0
当前温度为:25.0

在这个例子中,我们使用观察者模式实现了多个观察者对同一个被观察者状态的响应,并使得被观察者状态的变化自动触发所有观察者的更新。

iOS 中已有的 观察者设计模式实现

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。 简而言之,就是A和B,A对B的变化感兴趣,就注册为B的观察者,当B发生变化时通知A,告知B发生了变化。这个也叫做经典观察者模式。

Notification

对于感兴趣的A来说,在这里定义通知,也就是注册观察者(A就是观察者,怎么观察的以及观察到了会做些什么)

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notice:) name:@"tongzhi" object:nil];-(void)notice:(id)sender{  NSLog(@"%@",sender);
}

对于变化源B来说,在B这里发出通知

//创建通知对象
NSNotification *notification = [NSNotification notificationWithName:@"tongzhi" object:nil];//Name是通知的名称 object是通知的发布者(是谁要发布通知,也就是对象) userInfo是一些额外的信息(通知发布者传递给通知接收者的信息内容,字典格式)
//    [NSNotification notificationWithName:@"tongzhi" object:nil userInfo:nil];
//发送通知[[NSNotificationCenter defaultCenter] postNotification:notification];

当然了,还要移除观察者,在dealloc里面

- (void)dealloc {//删除根据name和对象,如果object对象设置为nil,则删除所有叫name的,否则便删除对应的[[NSNotificationCenter defaultCenter] removeObserver:self name:@"tongzhi" object:nil];
}

什么是通知机制或者说如何实现通知机制?

(假如让你实现,你会怎么做)
在这里插入图片描述

KVO

KVO全称叫Key Value Observing,顾名思义就是一种观察者模式用于监听属性的变化,KVO和NSNotification有很多相似的地方,用addObserver:forKeyPath:options:context方法 去观察,用removeObserver:forKeyPath:context去移除观察者,用observeValueForKeyPath:ofObject:change:context:去响应观察者

KVO监听属性的变化非常方便,下面举个例子

//自定义MyTimer类,在.h文件中定义一个属性name
@property (nonatomic, strong) NSString *name;
//自定义ViewController,在controller的.h文件中也定义一个属性myView
@property (nonatomic, strong) UIView *myView;

//在Viewcontroller的.m文件中定义一个button,设置点击事件,在该事件中分别调用上面定义的两个属性

int i = 5;
int sum = 15;
- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor whiteColor];UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];btn.frame = CGRectMake(100, 100, 100, 30);[btn setTitle:@"点击" forState:UIControlStateNormal];[btn addTarget:self action:@selector(handleTimer:) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:btn];_label = [[UILabel alloc ] initWithFrame:CGRectMake(100, 200, 180, 30)];_label.text = @"当前年龄15岁";[self.view addSubview:_label];//创建Mytimer对象_ourTimer = [[MyTimer alloc ] init];//观察属性name[_ourTimer addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew || NSKeyValueChangeOldKey context:nil];//观察属性myView[self addObserver:self forKeyPath:@"myView" options:NSKeyValueObservingOptionNew || NSKeyValueChangeOldKey context:nil];}
//点击事件,分别调用属性
- (void)handleTimer:(UIButton *)btn {_ourTimer.name = @"小明";self.myView = nil;NSLog(@"第一次设置名字");
}
//一旦属性被操作了,这里会自动响应(上面设置观察的属性才会在这响应)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {if ([keyPath isEqualToString:@"name"]) {NSLog(@"名字发生了改变");_label.text = [NSString stringWithFormat:@"当前年龄%d岁", i + sum];sum = i + sum;} else if ([keyPath isEqualToString:@"myView"]) {NSLog(@"我的视图");}
}
//移除
- (void)dealloc {[_ourTimer removeObserver:self forKeyPath:@"name"];[self removeObserver:self forKeyPath:@"myView"];
}

KVO底层实现

1、我们调用[self addObserver:self forKeyPath:@“highlighted” options:0 context:nil];
2、系统会动态自定义NSKVONotifying_A子类
3、重写setName,在内部恢复父类做法,通知观察者
4、如何让外界调用自定义A类的子类方法,修改当前对象的isa指针,指向NSKVONotifying_A
个人理解:isa指针改变,那么实例对象的isa指针指向类对象,类对象里面有实例对象的方法,所以isa指针改变,那么就回去调用新类的类对象方法,所以就能够实现调用子类的那个set方法。

重写set方法内部实现:
在这里插入图片描述
didChangeValueForKey方法内部会做出通知监听器,某个属性发生了变化。

如何实现手动KVO?

某个类实例对象直接调用这两个方法即可:
手动KVO就是调用了[self.person willChangeValueForKey:@””];
之后又调用了[self.person didChangeValueForKey:@””];
然后didChangeValueForKey内部实现就会触发KVO的回调。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/21008.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows 下载安装Redis,并配置开机自启动

windows 下载安装Redis&#xff0c;并配置开机自启动 1. 下载 地址&#xff1a;https://github.com/tporadowski/redis/releases Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择&#xff0c;这里我们下载 Redis-x64-xxx.zip压缩包&#xff0c;之后解压 打…

简要介绍 | 边缘计算:原理,研究现状与未来展望

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对边缘计算进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 边缘计算&#xff1a;原理&#xff0c;研究现状与未来展望 What is Edge Computing? | Moving Intelligence to the Edge 一、背景介…

滑动奇异频谱分析:数据驱动的非平稳信号分解工具(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SPSS方差分析

参考文章 导入准备好的数据 选择分析方法 选择参数 选择对比&#xff0c;把组别放入因子框中&#xff0c;把红细胞增加数放进因变量列表 勾选“多项式”&#xff0c;等级取默认“线性” &#xff0c;继续 接着点击“事后比较”&#xff0c;弹出对话框&#xff0c;勾选“LSD” …

C语言面试经典问题

当准备面试C语言相关职位时&#xff0c;以下是一些常见的C语言面试问题&#xff0c;可以帮助你准备面试。 什么是C语言&#xff1f; C语言的特点是什么&#xff1f; 请解释C语言中的标识符和关键字。 什么是C语言中的数据类型&#xff1f;请列举一些常见的数据类型。 如何…

十大医药数据查询软件(必备网站合集!)

医药数据查询软件从学科来看涵盖了医药学、药理学、临床医学、药物化学、药物分析、生物医药等领域的文献和信息&#xff0c;从类别区分涉及到医学、药学、经济学、法律等多个领域&#xff0c;医药数据查询软件的数据对于医药从业者、医学研究人员、政策制定者等都具有重要的参…

Vmware虚拟机网络配置回顾

如何配置Vmware里的虚拟机网络&#xff1f;这个东西不常用&#xff0c;都是自己练手用的。能用就行&#xff0c;千万不要花时间记&#xff0c;没意义。 很简单&#xff0c;照着敲 首先登陆自己的虚拟机 vim /etc/sysconfig/network-scripts/ifcfg-ens32 TYPE"Ethernet&q…

怎么学习Web框架和库相关知识?

学习Web框架和库相关知识可以帮助你构建高效、可扩展和安全的Web应用程序。以下是一些学习Web框架和库的方法和步骤&#xff1a; 确定学习目标&#xff1a; 明确你想学习的Web框架或库&#xff0c;例如常用的PHP框架&#xff08;如Laravel、Symfony&#xff09;或JavaScript库…

在linux中安装HAProxy

使用xfrp将压缩包上传到linux的opt目录下 1.解压HAProxy安装包 tar -zxvf haproxy-1.8.12.tar.gz 2. 查看点钱的内核及版本: uname -r 3. 根据内核版本选择编译参数 cd haproxy-1.8.12 cat README 4. 编译安装HAProxy make TARGETlinux2628 ARCHx86_64 PREFIX/usr/local…

【关于C++中----特殊类设计和单例模式】

文章目录 一、设计一个类&#xff0c;不能被拷贝1.1C98的实现方法及其弊端1.2 C11的实现方法 二、设计一个类&#xff0c;只能在堆上创建对象三、设计一个类&#xff0c;只能在栈上创建对象四、设计一个类&#xff0c;不能被继承五、设计一个类&#xff0c;只能创建一个对象(单…

Python微实践 - 诗意书香,宋风雅韵

诗意书香&#xff0c;宋风雅韵&#xff0c;宋代的文人们或婉约&#xff0c;或豪放&#xff0c;为后世留下了不朽的文学遗产 —— 宋词。宋词本质上是用于合乐的歌词&#xff0c;词人在填词时用的曲调名即为词牌。各位读者在中学时期一定对“水调歌头”、“念奴娇”这些词牌名耳…

音视频绕不开的话题之WebRTC

什么是WebRTC&#xff1f; 闲来无事&#xff0c;我们今天探讨下音视频绕不开的一个话题&#xff1a;WebRTC。WebRTC之于音视频行业&#xff0c;无异于FFMpeg&#xff0c;可以说WebRTC的开源&#xff0c;让音视频行业大跨步进入发展快车道。 WebRTC是一个支持实时音视频通信的开…