iOS——通知协议代理

news2024/9/21 4:38:37

通知

概要

  • 观察者和被观察者都无需知晓对方,只需要通过标记在NSNotificationCenter中找到监听该通知所对应的类,从而调用该类的方法。
  • 并且在NSNotificationCenter中,观察者可以只订阅某一特定的通知,并对其做出相应操作,而不用对某一个类发的所有通知都进行更新操作。
  • NSNotificationCenter对观察者的调用不是随机的,而是遵循注册顺序一一执行的,并且在该线程内是同步的

自定义实现通知方法:

通知的原理

通知机制的核心是一个与线程关联的单例对象叫通知中心(NSNotificationCenter)。通知中心发送通知给观察者是同步的,也可以用通知队列(NSNotificationQueue)异步发送通知。

数据结构

通知是一个单例。

static NSNotificationCenter *default_center = nil;

+ (NSNotificationCenter*) defaultCenter
{
  return default_center;
}

通知中心NSNotificationCenter的单例类中存放着两个表,一个存储所有注册通知信息的表的结构体,一个保存单个注册信息的节点结构体。

typedef struct NCTbl {
  Observation       *wildcard;  // 添加观察者时既没有传入 NotificationName ,又没有传入object,就会加在这个链表上,它里边的观察者可以接收所有的系统通知
  GSIMapTable       nameless;   // 添加观察者时没有传入 NotificationName 的表
  GSIMapTable       named;      // 添加观察者时传入了 NotificationName 的表
} NCTable
typedef struct  Obs {
  id        observer;   // 观察者对象
  SEL       selector;   // 方法信息
  struct Obs    *next;      // 指向下一个节点
  int       retained;   /* Retain count for structure.  */
  struct NCTbl  *link;      /* Pointer back to chunk table  */
} Observation;

解释一下NCTbl中三个表的作用:

  • name表:
    作用:
    管理通知名称和对应的观察者。

结构:
Key:通知名称(NotificationName),用于区分不同类型的通知。
Value:一个链表,保存所有注册了该通知名称的观察者。每个观察者可能会绑定一个特定的对象(object)。

对象 (object) 的作用:
当注册观察者时,可以指定一个对象(object),只监听该对象发出的通知。
如果 object 参数为 nil,系统会自动生成一个特殊的 nil 键,这个键对应的链表中保存了所有没有指定 object 的观察者。这样,nil 键可以用来管理所有未绑定特定对象的观察者。

在这里插入图片描述

  • nameless 表
    作用:管理没有指定通知名称的观察者。

结构:
Key:object,观察者对象。
Value:链表,保存所有注册了该对象的观察者。

nameless 表没有通知名称的约束,仅通过 object 来管理观察者。它的作用是处理那些未指定通知名称的情况,即所有未绑定特定通知名称的观察者。

在这里插入图片描述

  • wildcard 表
    作用:管理能够接收所有通知的观察者。

结构:
Value:链表,保存所有可以接收任何通知的观察者。

wildcard 表既没有通知名称 (NotificationName),也没有对象 (object)。它的链表存储了所有可以接收任意通知的观察者。这种设置通常用于管理那些对所有通知感兴趣的观察者。
 在这里插入图片描述

为什么 wildcard 需要使用链表?
动态插入和删除:
链表允许在运行时动态插入和删除节点,这对于通知系统来说非常重要,因为观察者可能在不同的时间点注册和注销。

管理多个观察者:
一个 wildcard 链表可以存储多个观察者,这些观察者对所有类型的通知感兴趣。因此,链表是一种有效的数据结构来管理这些观察者。

高效的遍历:
链表支持顺序遍历操作,这对于触发通知时遍历所有注册的观察者非常有用。

实现原理

使用方法addObserver:selector:name:object添加观察者流程总结:

  1. 首先会根据传入的参数实例化一个 Observation,Observation 对象保存了观察者对象,接收到通知观察者所执行的方法,以及下一个 Observation 对象的地址。
  2. 根据是否传入 NotificationName 选择操作 Named Table 还是 Nameless Table。
  3. 若传入了 NotificationName,则会以 NotificationName 为 key 去查找对应的 Value,若找到 value,则取出对应的 value;若未找到对应的 value,则新建一个 table,然后将这个 table 以 NotificationName 为 key 添加到 Named Table 中。
  4. 若在保存 Observation 的 table 中,以 object(接收通知的对象) 为 key 取对应的链表。若找到了则直接在链接末尾插入之前实例化好的 Observation;若未找到则以之前实例化好的 Observation 对象作为头节点插入进去。
  5. 若既没有 NotificationName 也没有 object,那么就加在 wildcard 链表中

使用方法postNotification发送通知流程总结:

  1. 首先会创建一个数组 observerArray 用来保存需要通知的 observer。
  2. 遍历 wildcard 链表(为了使接收所有通知的观察者也接收到消息),将 observer 添加到 observerArray 数组中。
  3. 若存在 object(接收通知的对象),在 nameless table 中找到以 object 为 key 的链表,然后遍历找到的链表,将 observer 添加到 observerArray 数组中。
  4. 若存在 NotificationName,在 named table 中以 NotificationName 为 key 找到对应的 table,然后再在找到的 table 中以 object 为 key 找到对应的链表,遍历链表,将 observer 添加到 observerArray 数组中。如果 object 不 为nil,则以 nil 为 key 找到对应的链表,遍历链表,将 observer 添加到 observerArray 数组中。
  5. 至此所有关于当前通知的 observer(wildcard + nameless + named)都已经加入到了数组 observerArray 中。遍历 observerArray 数组,取出其中 的observer 节点(包含了观察者对象和 selector)其中调用观察者的方法,调用形式如下:
[o->observer performSelector: o->selector withObject: notification];

移除通知的流程总结:

  1. 若 NotificationName 和 object 都为 nil,则清空 wildcard 链表。
  2. 若 NotificationName 为 nil,遍历 named table,若 object 为 nil,则清空 named table,若 object 不为 nil,则以 object 为 key 找到对应的链表,然后清空链表。在 nameless table 中以 object 为 key 找到对应的 observer 链表,然后清空,若 object 也为 nil,则清空 nameless table。
  3. 若 NotificationName 不为nil,在 named table 中以 NotificationName 为 key 找到对应的 table,若 object 为 nil,则清空找到的 table,若 object 不为 nil,则以 object 为 key 在找到的 table 中取出对应的链表,然后清空链表。

通知的发送时同步的,还是异步的?发送消息与接收消息的线程是同一个线程么?
通知中心发送通知给观察者是同步的,也可以用通知队列(NSNotificationQueue)异步发送通知。

NSNotificationQueue(通知队列)

NSNotificationQueuenotification Center的缓冲池。如果我们使用普通的postNotification这种方法来发送通知,那么这个通知就会直接发送到notification Centernotification Center则会直接将其发送给注册了该通知的观察者。但是如果我们使用NSNotificationQueue就不一样了,通知不是直接发送给notification Center,而是先发送给NSNotificationQueue,然后由NSNotificationQueue决定在当前runloop结束或者空闲的时候转发给notification Center,再由notification转发给注册的观察者。通过NSNotificationQueue,可以合并重复的通知,以便只发送一个通知。

NSNotificationQueue遵循FIFO的顺序,当一个通知移动到NSNotificationQueue的最前面,它就被发送给notification Center,然后notification Center再将通知转发给注册了该通知的监听者。
每一个线程都有一个默认的NSNotificationQueue,这个NSNotificationQueue和通知中心联系在一起。当然我们也可以自己创建NSNotificationQueue,可以为一个线程创建多个NSNotificationQueue

单例模式

系统为我们提供的单例类有:

UIApplication(应用程序实例类)
NSNotificationCenter(消息中心类)
NSFileManager(文件管理类)
NSUserDefaults(应用程序设置)
NSURLCache(请求缓存类)
NSHTTPCookieStorage(应用程序cookies池)

单例的GCD写法:

#import "AModel.h"

static id _instance = nil;
@implementation AModel

+ (id)sharInstance {
    return [[self alloc] init];
}

//将我们原先写在自定义初始化方法中的内容写到allocWithZone中
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}

//重写 copyWithZone:方法,避免实例对象的 copy 操作导致创建新的对象
-(instancetype)copyWithZone:(NSZone *)zone
{
    //由于是对象方法,说明可能存在_instance对象,直接返回即可
    return _instance;
}

@end

代理模式

协议是多个类(或者说对象)之间协商的一个公共接口,提供一系列方法的声明给类们使用;而代理是协议的一个典型应用机制。
代理模式的核心思想是通过代理接口分离使用者和服务提供者,降低了模块之间的耦合度。在实际中灵活使用代理模式可以让我们写出更优质的模块结构的代码。

代理就是自己不想做的事情,让代理做。(类似老板和秘书)
在这里插入图片描述

代理设计模式的场合:

  1. 当对象A发生了一些行为,想告知对象B(让对象B成为对象A的代理对象)
  2. 对象B想监听对象A的一些行为(让对象B成为对象A的代理对象)
  3. 当对象A无法处理某些行为的时候,想让对象B帮忙处理(让对象B成为对象A的代理对象)

协议的编写规范:

  1. 一般情况下, 当前协议属于谁, 我们就将协议定义到谁的头文件中
  2. 协议的名称一般以它属于的那个类的类名开头, 后面跟上protocol或者delegate
  3. 协议中的方法名称一般以协议���名称protocol之前的作为开头
  4. 一般情况下协议中的方法会将触发该协议的对象传递出去
  5. 一般情况下一个类中的代理属于的名称叫做 delegate
  6. 当某一个类要成为另外一个类的代理的时候, 一般情况下在.h中用@protocol 协议名称;告诉当前类 这是一个协议.在.m中用#import真正的导入一个协议的声明

注意:

  1. 协议不能声明成员变量,不能写实现
  2. 只要父类遵守了某个协议,那么子类也遵守
  3. 协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明

协议中有2个关键字可以控制方法是否要实现(默认是@required,在大多数情况下,用途在于程序员之间的交流)

  • @required:这个方法必须要实现(若不实现,编译器会发出警告)
  • @optional:这个方法不一定要实现

代理的原理

在iOS中代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。
请添加图片描述

这里,将代理对象进行弱引用的作用是为了避免循环引用

代理的循环引用

比如以下代码:B强引用A,而A的delegate属性指向B,这里的delegate是用strong修饰的,所以A也会强引用B,这是一个比较典型的循环引用样例。所以要将代理delegate改为弱引用weak。

@protocol ClssADelegate
- (void)eat;
@end

@interface ClassA : UIViewController
@property (nonatomic, strong) id  delegate;
@end

//ClassB:
@interface ClassB ()
@property (nonatomic, strong) ClassA *classA;
@end

@implementation ClassB
- (void)viewDidLoad {
    [super viewDidLoad];
    self.classA = [[ClassA alloc] init];
    self.classA.delegate = self;
}


请添加图片描述

请添加图片描述

设计模式总结
KVO/通知 -------> 观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
优势:解耦合
接口隔离原则、开放-封闭原则

KVC --------> KVC模式
单例模式

利用应用程序只有一个该类的实例对象这一特殊性来实现资源共享。
优势:使用简单,延时求值,易于跨模块
劣势:这块内存知道程序退出时才能释放
单一职责原则
举例:[UIApplication sharedApplication]。

代理模式

委托方将不想完成的任务交给代理方处理,并且需要委托方通知代理方才能处理。
优势: 解耦合
开放-封闭原则
举例:tableview的数据源和代理

策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
优势:使算法的变化独立于使用算法的用户
接口隔离原则、多用组合,少用继承、针对接口编程,而非实现
举例:账号密码输入格式的判断、NSArray的sortedArrayUsingSelector等等

MVC模式

将程序书写分为三层,分别为模型、视图、控制器,每层都有各自的职责完成各自的工作。
优势: MVC模式使系统,层次清晰,职责分明,易于维护
对扩展开放-对修改封闭

MVVM模式

用于解决MVC模式下C层代码冗杂的情况(过多的网络请求以及业务逻辑处理)而出现的MVVM模式,其相比于MVC多了一层ViweModel(业务处理和数据转化)层,专门用于处理数据。
当功能简单时,MVVM反而会增加很多代码,所以对于简单的功能,MVC更加的方便。

MVP模式

MVP是MVC模式派生出来的,常用于创建用户界面,在MVP中所有页面显示逻辑都会被推送到presenter(中间人)中,它主要实现程序逻辑控制、数据的检索,并格式化数据以便在视图中显示,把Model和View完全的进行分离,MVP模式的V是View + ViewController。

三种工厂模式

通过给定参数来返回对应的实例,完全对用户隐藏其实现的原理。
优势:易于替换,面向抽象编程
依赖倒置原则

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《论多源数据集成及应用》写作框架,软考高级系统架构设计师

论文真题 在如今信息爆炸的时代,企业、组织和个人面临着大量的数据。这些数据来自不同的渠道和资源,包括传感器、社交媒体、销售记录等,它们各自具有不同的数据格式、分布和存储方式。因此如何收集、整理和清洗数据,以建立一个一致、完整的数据集尤为重要。多源数据集成可…

Leetcode 700-二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。 题解 如果root.val>val,则搜索左子树,如果roo…

finalshell 4.5.x在m1mac闪退

使用过程中会出现突然闪退,尤其在定位生产打开一堆窗口的情况下,绝绝子 闪退崩溃日志: Thread 116 Crashed:: Java: pool-4-thread-28 0 libsystem_kernel.dylib 0x18e926600 __pthread_kill 8 1 libsystem_pthread.dyl…

基于opencv实现双目立体匹配点云距离

双目相机或两个单目相机。 一、相机标定 MATLAB软件,打开双目标定app。 点击add images,弹出加载图像的窗口,分别导入左图和右图,设置黑白格长度(标定板的长度一般为20)。 点击确定,弹出加载…

ArrayList,LinkedList

ArrayList集合 底层原理 1.利用空参创建的集合,在底层创建一个默认长度为0的数组 2.添加第一个元素时,底层会创建一个新的长度为10的数组 3.存满时,会扩容1.5倍 4.如果一次添加多个元素,1.5倍还放不下,则新创建数…

【C++】list的使用与简单模拟实现

目录 1、list的介绍和使用: 1、结构: 2、接口函数: 迭代器遍历: 增删查改: 翻转与排序: 2、list的模拟实现: 1、节点的封装: 2、迭代器的封装: 3、list的模拟实…

Flutter中自定义气泡框效果的实现

在用户界面的设计中,气泡框(Bubble)是一种非常有效的视觉工具,它可以用来突出显示信息或提示用户。气泡框广泛应用于聊天应用、通知提示等场景。在 Flutter 中,虽然有很多现成的气泡框组件,但如果你想要更多…

关于 ubuntu系统install的cmake版本较低无法编译项目升级其版本 的解决方法

若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/141933927 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…

django摄影竞赛小程序论文源码调试讲解

2系统关键技术及工具简介 系统开发过程中设计的关键技术是系统的核心,而开发工具则会影响的项目开发的进程和效率。第二部分便描述了系统的设计与实现等相关开发工具。 2.1 Python简介 Python 属于一个高层次的脚本语言,以解释性,编译性&am…

Android Studio调试Flutter项目

run运行项目中途,点击Flutter Attach 等一会就可以调试! 或者,直接Debug允行项目。

C++相关概念和易错语法(32)(单例模式、类型转换)

1.单例模式 (1)设计模式是什么? 简单来说,被反复使用,多数人知晓、经过分类的代码设计经验的总结就叫设计模式,它建立在特殊类的设计之上,实现特殊的功能,运用的知识也十分综合。如…

协议集合(学习笔记)

按照数据的传送方式,通信协议可分为以下2种。 串行通信:串行(Serial)指的是逐个传输数据位,一次只传输一个位。 并行通信:并行(Parallel)指的是同时传输多个数据位,一次…

VMware 中 kali Linux的安装与使用

文章目录 前言 一、安装虚拟机 二、使用步骤 总结 前言 随着信息技术的飞速发展,虚拟化技术已经成为现代企业和个人用户不可或缺的一部分。通过虚拟化技术,我们可以在一台物理计算机上运行多个独立的操作系统和应用程序,从而实现资源的高效利…

基于WiFi的智能照明控制系统的设计与实现(论文+源码)

1系统方案设计 本设计智能照明控制系统,结合STM32F103单片机、光照检测模块、显示模块、按键模块、太阳能板、LED灯模块、WIFI模块等器件构成整个系统,在功能上可以实现光照强度检测,并且在自动模式下可以自动调节照明亮度,在手动…

【spring】例子2:mvc web开发

领域类 开发时编译时用lombok提供支持 最终生成的包里不包含lombok

【Android】程序开发组件—探究Jetpack

引言 Jetpack是一个开发组件工具集,它的主要目的是帮助我们编写出更加简洁的代码,并简化我们的开发过程,在这么多的组件当中,最需要我们关注的其实还是架构组件,接下来就对Jetpack的主要架构组件进行学习!…

数据结构-----栈、队列

一、栈 1、栈(stack)是限定仅在表尾进行插入和删除操作的线性表。 把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表,简称L…

OpenAI gym: Trouble installing Atari dependency (Mac OS X)

题意: 使用OpenAI Gym库时,安装Atari环境可能会遇到一些依赖问题,尤其是在Mac OS X系统上 问题背景: Im new to OpenAI gym. Ive successfully installed OpenAI gym on my Mac OS X (High Sierra 10.13.3) laptop and made a D…

C语言程序设计(算法的概念及其表示)

一、算法的概念 一个程序应包括两个方面的内容: 对数据的描述:数据结构 对操作的描述:算法 著名计算机科学家沃思提出一个公式: 数据结构 +算法 =程序 完整的程序设计应该是: 数据结构+算法+程序设计方法+语言工具 广义地说,为解决一个问题而采取的方法和步骤…

学不会虚拟列表?10分钟带你实现高度固定的Vue虚拟列表方案及原理

前言 本文主要介绍长列表的一种优化方案:虚拟列表。本文主要是对传统的虚拟列表方案进行更加详尽的刨析,以便我们能够更加深入理解虚拟列表的原理。 虚拟列表目录 1、为什么需要使用虚拟列表2、什么是虚拟列表与懒加载的区别(重要) 3、实现思路4、通过节…